Right ... Inner classes in Scala are a bit difficult. Try a simple example before I show you the rewritten version of the code you provided.
case class Foo(x: Int) { case class Bar(y: String) }
Now consider the following code snippet:
val x = new Foo(1) val y = new Foo(2) val a = new x.Bar("one") val b = new y.Bar("two")
The most common type of a and b is Foo#Bar , which means the inner class Bar with any external object of type Foo . But we could more specifically say that type a is equal to x.Bar , and type b is y.Bar - this means that a is an instance of the inner class Bar with an external object x , similar to b .
In fact, you can see that the types differ by calling typeOf(a) and typeOf(b) , where typeOf is the utility method defined as such. (he just gives the type of his argument with a pretty good output type and uses Manifest s a bit)
def typeOf[T](x: T)(implicit m: scala.reflect.Manifest[T]) = m.toString
Since the internal object contains a link to its enclosing object, you cannot create an instance of the internal object without specifying its external object. Therefore, you can call new x.Bar("one") , but you cannot call new Foo#Bar("?") - as in the second case, you did not specify what is the internal object for the new object that you are trying to build.
So, back to the code snippet. When you are pattern matching, you actually call the constructor - when you call C1(e1) . Since C1 is an alias for Container[TKey]#C1 , you tried to call the constructor of the inner class without specifying its outer object, which does not work for the reasons stated above. The way I write the code will be as follows:
trait Container[TKey] { abstract trait CB case class C1(val e : AnyRef) extends CB case class C2(val e : AnyRef) extends CB } class DoStuff[TKey] (val c: Container[TKey], val element: Container[TKey]#CB) { element match { case c.C1(e1) => Some(e1) case c.C2(e2) => Some(e2) case _ => None } }
Now it compiles and hopefully does what you want. But take this with great care! Due to type erasure, Scala cannot guarantee that element is of type c.CB or type d.CB , where CB is the same for c and d .
Consider the following example:
def matcher(arg: Foo#Bar) = { arg match { case x.Bar(n) => println("x"); case y.Bar(n) => println("y"); } }
where x and y are still. Try running the following:
matcher(a) matcher(b)
They both type x !
Therefore, I would rewrite the code to explicitly have an element in the container:
trait Container[TKey] { abstract trait CB case class C1(val e : AnyRef) extends CB case class C2(val e : AnyRef) extends CB val element: CB } class DoStuff[TKey](val c: Container[TKey]) { c.element match { case c.C1(e1) => Some(e1) case c.C2(e2) => Some(e2) case _ => None } }
Hope this helps :)
- Flavy Chipchigan