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 unapply
of the implicit ClassTag
? If I understand the docs correctly, the unapply
from the tag will return a None if attr
does not fully match its type. If it does match, it will return a Some of type A[S]
. So refactored to use unapply, your code would look like this:
def apply[A[_] <: Attribute[_]](key: String)
(implicit tag: reflect.ClassTag[A[S]]): Option[A[S]] =
get(key) match {
case Some(attr) => tag.unapply(attr)
case _ => None
}