I am looking for a way to remove type parameter S from the call to apply in the following example:
object Attribute {
trait Int [S] extends Attribute[S]
trait Boolean[S] extends Attribute[S] // etc.
}
sealed trait Attribute[S]
trait Attributes[S] {
protected def get(key: String): Option[Attribute[S]]
def apply[A <: Attribute[S]](key: String)
(implicit tag: reflect.ClassTag[A]): Option[A] =
get(key) match {
case Some(attr: A) => Some(attr)
case _ => None
}
}
With the above definition, a test case would be:
trait Test[S] {
def map: Attributes[S]
map[Attribute.Int[S]]("foo")
}
What I'm trying to do, is modify the apply definition to allow the following:
trait Test2[S] {
def map: Attributes[S]
map[Attribute.Int]("foo") // use partially applied attribute type
}
EDIT: So following up on the suggestion of Marius and the comments, why is the following still producing an erasure warning:
import reflect.runtime.universe._
trait Attributes[S] {
protected def get(key: String): Option[Attribute[S]]
def apply[A[x] <: Attribute[x]](key: String)
(implicit tag: TypeTag[A[S]]): Option[A[S]] =
get(key) match {
case Some(attr: A[S]) => Some(attr)
case _ => None
}
}
To me that clearly doesn't make sense. On the one hand I have the full type tag for A[S] available. On the other hand, it should even work completely in its absence, as Attribute is invariant in S, so if I get an Option[Attribute[S]], and I match against Some(attr: A[x]), the only possibility is that x == S.
EDIT 2: The condition for the solution is that the shape of Attribute trait is not changed, e.g. not moving type constructor parameter S to a member field.
Have you considered leveraging the
unapplyof the implicitClassTag? If I understand the docs correctly, theunapplyfrom the tag will return a None ifattrdoes not fully match its type. If it does match, it will return a Some of typeA[S]. So refactored to use unapply, your code would look like this: