As shown below, in Haskell, it's possible to store in a list values with heterogeneous types with certain context bounds on them:
data ShowBox = forall s. Show s => ShowBox s
heteroList :: [ShowBox]
heteroList = [ShowBox (), ShowBox 5, ShowBox True]
How can I achieve the same in Scala, preferably without subtyping?
The
ShowBox
example you gave involves an existential type. I'm renaming theShowBox
data constructor toSB
to distinguish it from the type:We say
s
is "existential", but theforall
here is a universal quantifier that pertains to theSB
data constructor. If we ask for the type of theSB
constructor with explicitforall
turned on, this becomes much clearer:That is, a
ShowBox
is actually constructed from three things:s
s
Show s
.Because the type
s
becomes part of the constructedShowBox
, it is existentially quantified. If Haskell supported a syntax for existential quantification, we could writeShowBox
as a type alias:Scala does support this kind of existential quantification and Miles's answer gives the details using a trait that consists of exactly those three things above. But since this is a question about "forall in Scala", let's do it exactly like Haskell does.
Data constructors in Scala cannot be explicitly quantified with forall. However, every method on a module can be. So you can effectively use type constructor polymorphism as universal quantification. Example:
A Scala type
Forall[F]
, given someF
, is then equivalent to a Haskell typeforall a. F a
.We can use this technique to add constraints to the type argument.
A value of type
F SuchThat G
is like a value of the Haskell typeforall a. G a => F a
. The instance ofG[A]
is implicitly looked up by Scala if it exists.Now, we can use this to encode your
ShowBox
...The
ShowBox.apply
method is the universally quantified data constructor. You can see that it takes a typeS
, an instance ofShow[S]
, and a value of typeS
, just like the Haskell version.Here's an example usage:
A more direct encoding in Scala might be to use a case class:
Then:
In this case, a
List[ShowBox]
is basically equivalent to aList[String]
, but you can use this technique with traits other thanShow
to get something more interesting.This is all using the
Show
typeclass from Scalaz.Why not:
As the authorities' answers suggested, I'm often surprised that Scala can translate "Haskell type monsters" into very simple one.
I don't think a 1-to-1 translation from Haskell to Scala is possible here. But why don't you want to use subtyping? If the types you want to use (such as Int) lack a show method, you can still add this via implicit conversions.
( Edit: Adding methods to show, to answer comment. )
I think you can get the same using implicit methods with context bounds:
As @Michael Kohl commented, this use of forall in Haskell is an existential type and can be exactly replicted in Scala using either the forSome construct or a wildcard. That means that @paradigmatic's answer is largely correct.
Nevertheless there's something missing there relative to the Haskell original which is that instances of its ShowBox type also capture the corresponding Show type class instances in a way which makes them available for use on the list elements even when the exact underlying type has been existentially quantified out. Your comment on @paradigmatic's answer suggests that you want to be able to write something equivalent to the following Haskell,
@Kim Stebel's answer shows the canonical way of doing that in an object-oriented language by exploiting subtyping. Other things being equal, that's the right way to go in Scala. I'm sure you know that, and have good reasons for wanting to avoid subtyping and replicate Haskell's type class based approach in Scala. Here goes ...
Note that in the Haskell above the Show type class instances for Unit, Int and Bool are available in the implementation of the useShowBox function. If we attempt to directly translate this into Scala we'll get something like,
and this fails to compile in useShowBox as follows,
The problem here is that, unlike in the Haskell case, the Show type class instances aren't propagated from the ShowBox argument to the body of the useShowBox function, and hence aren't available for use. If we try to fix that by adding an additional context bound on the useShowBox function,
this fixes the problem within useShowBox, but now we can't use it in conjunction with map on our existentially quantified List,
This is because when useShowBox is supplied as an argument to the map function we have to choose a Show instance based on the type information we have at that point. Clearly there isn't just one Show instance which will do the job for all of the elements of this list and so this fails to compile (if we had defined a Show instance for Any then there would be, but that's not what we're after here ... we want to select a type class instance based on the most specific type of each list element).
To get this to work in the same way that it does in Haskell, we have to explicitly propagate the Show instances within the body of useShowBox. That might go like this,
then in the REPL,
Note that we've desugared the context bound on ShowBox so that we have an explicit name (showInst) for the Show instance for the contained value. Then in the body of useShowBox we can explicitly apply it. Also note that the pattern match is essential to ensure that we only open the existential type once in the body of the function.
As should be obvious, this is a lot more vebose than the equivalent Haskell, and I would strongly recommend using the subtype based solution in Scala unless you have extremely good reasons for doing otherwise.
Edit
As pointed out in the comments, the Scala definition of ShowBox above has a visible type parameter which isn't present in the Haskell original. I think it's actually quite instructive to see how we can rectify that using abstract types.
First we replace the type parameter with an abstract type member and replace the constructor parameters with abstract vals,
We now need to add the factory method that case classes would otherwise give us for free,
We can now use plain ShowBox whereever we previously used ShowBox[_] ... the abstract type member is playing the role of the existential quantifier for us now,
(It's worth noting that prior to the introduction of explict forSome and wildcards in Scala this was exactly how you would represent existential types.)
We now have the existential in exactly the same place as it is in the original Haskell. I think this is as close to a faithful rendition as you can get in Scala.