views:

2111

answers:

3

It's a sad fact of life on Scala that if you instantiate a List[Int], you can verify that your instance is a List, and you can verify that any individual element of it is an Int, but not that it is a List[Int], as can be easily verified:

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

The -unchecked option puts the blame squarely on type erasure:

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

Why is that, and how do I get around it?

+34  A: 

Scala was defined with Type Erasure because the Java Virtual Machine (JVM), unlike Java, did not get generics. This means that, at run time, only the type exists, not its parameters. In the example, JVM knows it is handling a scala.collection.immutable.List, but not that this list is parameterized with Int.

Fortunately, an obscure, and experimental, feature of Scala let you get around that. It’s the Manifests. A Manifest is class whose instances are objects representing types. Since these instances are objects, you can pass them around, store them, and generally call methods on them. With the support of implicit parameters, it becomes a very powerful tool. Take the following example, for instance:

object Registry {
  import scala.reflect.Manifest

  private var _map= Map.empty[Any,(Manifest[_], Any)] 

  def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
    _map = _map(name) = (m, item)
  }

  def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
    val o = _map.get(key)

    o match {
      case Some((om: Manifest[_], s : Any)) =>
        if(om <:< m) Some(s.asInstanceOf[T]) else None
      case _ => None
    }
  }
}

scala> Registry.register("a", List(1,2,3))

scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))

scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None

When storing an element, we store a "Manifest" of it too. A Manifest is a class whose instances represent Scala types. These objects have more information than JVM does, which enable us to test for the full, parameterized type.

As mentioned, this feature is experimental, and there are cases in which it doesn't work. Still, it can go a long way.

Daniel
Hmm... so did you post the question already knowing the answer, or what?
skaffman
It's not such a bad idea to pose some well-phrased questions and then answer them yourself. SO is supposed to be a programming question and answer site after all. The more good answers to good questions the better
oxbow_lakes
This is not only allowed, but endorsed by Stack Overflow's FAQ. I was looking for another thing when I saw it, and I thought this was an interesting topic not covered before. Of course, I can't accept my answer unless it is upvoted, and both question and answer can be downvoted if someone takes an issue with it.
Daniel
+1 I applaud posting thoughtful answers to good questions (i.e those you struggled with) - not only should it be endorsed, I feel it shold be strongly encouraged - thanks for the info on manifests - i'm still very new to scala so reading code takes me time - but it's good to know such metaobjects exist :)
Faisal Vali
I agree that answering these kinds of questions is a good idea : I had read about this *somewhere* before, but it is *much easier* to find on stack overflow.
Tristan Juricek
This reminds me of the Typesafe Heterogeneous Container popularized by Josh Bloch in Effective Java. And like tricks like Gafter's Gadget aka Super Type Tokens. If you're looking for further reading in the Java direction....
Alex Miller
"Alas, " should really be changed to "Fortunately, ", it gives the wrong impression :)
Dimitris Andreou
@Dimitris Ok, modified as suggested.
Daniel
+1  A: 

I can't comment yet :/ But what concerns me is that this information (question/answer), especially being phrased like this, really belongs in the Scala documentation.

If someone is going to go all the trouble of posing a question and then answering it himself, when it's as thoughtful, informative, intentional and as internal (to the workings of Scala) as this then that effort should instead be spend on documenting it properly where it belongs.

Does this already exist in Scala documentation anywhere?

Don't get me wrong Daniel - I love your work :P

Antony Stubbs
Antony, Stack Overflow has two advantages over any documentation ever: it's searchable, and if the answer isn't there you can post the question. Manifest documentation is not officially available because it's officially experimental -- as indicated in the answer.
Daniel
+1  A: 

Scala 2.8 Beta 1 RC4 just made some changes to how type erasure works. I'm not sure if this directly affects your question.

Scott Morrison
That's just what types erasure _to_, that has changed. The short of it can be summed as "_Proposal: The erasure of "Object with A" is "A" instead of "Object"._" The actual specification is rather more complex. It's about mixins, at any rate, and this question is concerned about generics.
Daniel
Thanks for the clarification -- I'm a scala newcomer. I feel like right now is a bad time to jump into Scala. Earlier, I could have learnt the changes in 2.8 from a good base, later I'd never have to know the difference!
Scott Morrison