Several Scala features interact to give this behavior. Firstly, Manifest not only added to the secret list of implicit parameters in the constructor, but also in the copy method. It is well known that
case class Foo[+A : Manifest](a: A)
is just syntactic sugar for
case class Foo[+A](a: A)(implicit m: Manifest[A])
but it also affects the copy constructor, which will look like this:
def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)
All those implicit m are created by the compiler and sent to the method through a list of implicit parameters.
It would be nice if one used the copy method in the place where the compiler knew a parameter of type Foo . For example, this will work outside the Bar class:
val foo = Foo(1) val aCopy = foo.copy() println(aCopy.myManifest) // Prints "Int"
This works because the compiler reports that Foo is Foo[Int] , so it knows that foo.a is Int , so it can call copy like this:
val aCopy = foo.copy()(manifest[Int]())
(Note that manifest[T]() is a function that creates a manifest representation of type T , for example Manifest[T] with a capital of "M". Not shown, adding a default parameter to copy .) It also works in the Foo class because it already has a manifest that was passed when the class was created. It will look something like this:
case class Foo[+A : Manifest](a: A) { def myManifest = implicitly[Manifest[_ <: A]] def localCopy = copy() } val foo = Foo(1) println(foo.localCopy.myManifest) // Prints "Int"
However, in the original example, it fails in the Bar class due to the second peculiarity: while Bar type parameters are known in the Bar class, type parameter parameters are not type, He knows that A in Bar is Foo or SubFoo or SubSubFoo , but not if it's Foo[Int] or Foo[String] . This, of course, is a well-known type erasure problem in Scala, but here the problem is here, even if the class doesn't seem to do anything with the type parameter of type Foo . But this, remember, there is a secret injection of the manifest every time copy is called, and this shows a rewriting of those that were there before. Since the Bar class has no idea if there was a parameter of type Foo , it simply creates an Any manifest and sends it as follows:
def fooCopy = foo.copy()(manifest[Any])
If someone has control over the Foo class (for example, this is not a List ), then the workaround is to do all the copying in the Foo class by adding a method that will perform the correct copy, for example localCopy above and return the result:
case class Bar[A <: Foo[Any]](foo: A) { //def fooCopy = foo.copy() def fooCopy = foo.localCopy } val bar = Bar(Foo(1)) println(bar.fooCopy.myManifest) // Prints "Int"
Another solution is to add a parameter of type Foo as an expressed parameter of type Bar :
case class Bar[A <: Foo[B], B : Manifest](foo: A) { def fooCopy = foo.copy() }
But this does not scale well if the class hierarchy is large (i.e. more members have type parameters, and these classes also have type parameters), since each class must have type parameters of each class below. It looks like when trying to create a Bar :
val bar = Bar(Foo(1)) // Does not compile val bar = Bar[Foo[Int], Int](Foo(1)) // Compiles