I have a type class which I'd like to use to store a type of an object:
trait RetainType {
type A
}
object RetainType {
def apply[A0](): RetainType = new RetainType{
type A = A0
}
}
trait Annotation
class Entity extends Annotation
RetainType.A
Annotation
def hasAnnotation[A <: Annotation] = Unit
RetainType
val retainType = RetainType[Entity]
hasAnnotation[RetainType.A] //throws an error: type arguments [retainType.A] do not conform to method hasAnnotation's type parameter bounds [A <: Annotation]
hasAnnotation[Entity] //works fine
You messed up the signature of RetainType.apply
:
def apply[A0](): RetainType
The return type does not mention A0
, so it is "forgotten". That is, in
val x = RetainType[Int]
x.A
is completely abstract; the compiler cannot prove that x.A = Int
because apply
's signature erased that information. Use a refinement type:
object RetainType {
def apply[A0](): RetainType { type A = A0 } = new RetainType { override type A = A0 }
}
You may want to use the Aux
pattern to make this nicer to work with:
object RetainType {
type Aux[A0] = RetainType { type A = A0 }
def apply[A0](): RetainType.Aux[A0] = new RetainType { override type A = A0 }
}