Search code examples
scalamanifestabstract-type

Missing class manifest for Array of abstract type member


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?


Solution

  • 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.