Missing class manifest for Array of abstract type member

216 Views Asked by At

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?

2

There are 2 best solutions below

1
On BEST ANSWER

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.

0
On

I could come up with one solution that involves a simple indirection:

trait Sys[Self <: Sys[Self]] {
  type R[A] <: Ref[Self, A]
  def newRefArray[A](size: Int): Array[Self#R[A]]
}

trait Struct[S <: Sys[S], A] {
  def sys: S
  val arr = sys.newRefArray[A](1)
}