I am looking for recommendations of providing a class manifest in an array instantiation. I have refactored this code (which compiles fine):
trait Ref[A]
trait Struct[A] {
val arr = new Array[Ref[A]](1)
}
to this:
trait Ref[S <: Sys[S], A]
trait Sys[Self <: Sys[Self]] {
type R[A] <: Ref[Self, A]
}
trait Struct[S <: Sys[S], A] {
val arr = new Array[S#R[A]](1)
}
This fails with message "cannot find class manifest for element type S#R[A]"
So how would I solve this?
Your problem is that Array
being invariant in its type parameter, requires a precise type argument. Your definition of type R
in Sys
is only providing an upper bound.
You can fix the problem at the definition site by replacing the upper bound on R
with an equality,
trait Ref[S <: Sys[S], A]
trait Sys[Self <: Sys[Self]] {
type R[A] = Ref[Self, A] // '=' not '<:'
}
trait Struct[S <: Sys[S], A] {
val arr = new Array[S#R[A]](1) // OK
}
Alternatively, if you'd prefer to leave R[A]
open in Sys
you can specify the equality constraint at the use site via a refinement, like so,
trait Ref[S <: Sys[S], A]
trait Sys[Self <: Sys[Self]] {
type R[A] <: Ref[Self, A] // Back to an upper bound
}
trait Struct[S <: Sys[S] { type R[A] = Ref[S, A] }, A] {
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// Assert the equality via a refinement
val arr = new Array[S#R[A]](1) // OK
}
If you can't pin down type R
in either of these ways, then you have no option but to provide the ClassManifest explicitly yourself,
trait Ref[S <: Sys[S], A]
trait Sys[Self <: Sys[Self]] {
type R[A] <: Ref[Self, A]
}
trait Struct[S <: Sys[S], A] {
implicit val rM : ClassManifest[S#R[A]] // Provided manifest later ...
val arr = new Array[S#R[A]](1) // OK
}
class SomeSys extends Sys[SomeSys] {
type R[A] = Ref[SomeSys, A]
}
val s = new Struct[SomeSys, Int] {
val rM = implicitly[ClassManifest[SomeSys#R[Int]]]
// ^^^^^^^^^^^^^^
// Precise type known here
}
Which of these to pick depends very much on your larger context.