Reputation: 3032
I have a code
case class MyTypeTag[T] ()
def getTypeTags[TT <: Product : TypeTag] = {
val subtypesTags: List[MyTypeTag[Option[_]] = ???
sybtypesTags
}
val res = getTypeTags[(Int, String, Boolean)]
// res = MyTypeTag[Option[Int]] :: MyTypeTag[Option[String]] :: MyTypeTag[Option[Boolean]] :: Nil
so i want to call function getTypeTags
passing any tuple type as type parameter and get list of MyTypeTag
instances with each type inside tuple wrapped in Option
if in intelliJ i evaluate expression typeof[TT]
i see property args
with list of my types, but i do not know how to access from code. Or may be some other ways can be apllied.
Thanks in advance
Upvotes: 0
Views: 317
Reputation: 51648
Type parameter T
in case class MyTypeTag[T]()
must be known at compile time. But it seems you try to define it using runtime reflection. This can't work (unless you define the class at runtime: toolbox.define(q"case class MyTypeTag[T]()")
but this would be tricky).
You can try to use compile-time reflection
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def getTypeTags[TT <: Product]: List[MyTypeTag[_ <: Option[_]]] =
macro getTypeTagsImpl[TT]
def getTypeTagsImpl[TT: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
weakTypeOf[TT].typeArgs.map(t => q"MyTypeTag[Option[$t]]()")
.foldRight[Tree](q"Nil")((t, ts) => q"$t :: $ts")
}
Usage:
getTypeTags[(Int, String, Boolean)] //List(MyTypeTag(), MyTypeTag(), MyTypeTag())
In order to verify that the code works properly let's temporarily modify MyTypeTag
import scala.reflect.runtime.universe.TypeTag
case class MyTypeTag[T]()(implicit val typeTag: TypeTag[T])
val res = getTypeTags[(Int, String, Boolean)]
res(0).typeTag // TypeTag[Option[Int]]
res(1).typeTag // TypeTag[Option[String]]
res(2).typeTag // TypeTag[Option[Boolean]]
You can also use Shapeless instead of macros
import shapeless.ops.hlist.{FillWith, Mapped, ToList}
import shapeless.{Generic, HList, Poly0}
case class MyTypeTag[T]()
def getTypeTags[TT <: Product] = new {
def apply[L <: HList, L1 <: HList]()(implicit
generic: Generic.Aux[TT, L],
mapped: Mapped.Aux[L, λ[X => MyTypeTag[Option[X]]], L1],
fillWith: FillWith[myTypeTagPoly.type, L1],
toList: ToList[L1, MyTypeTag[_ <: Option[_]]]
): List[MyTypeTag[_ <: Option[_]]] =
fillWith().toList
}
object myTypeTagPoly extends Poly0 {
implicit def cse[A]: Case0[MyTypeTag[Option[A]]] = at(MyTypeTag[Option[A]]())
}
getTypeTags[(Int, String, Boolean)]() // List(MyTypeTag(), MyTypeTag(), MyTypeTag())
If you make MyTypeTag
covariant (MyTypeTag[+T]
) then List[MyTypeTag[_ <: Option[_]]]
can be replaced with List[MyTypeTag[Option[_]]]
.
Upvotes: 1
Reputation: 12091
You can't distinguish between an instance of MyTypeTag[Int]
and an instance of MyTypeTag[String]
at runtime (to check this for yourself, try
val l = List(MyTypeTag[Option[Int]](), MyTypeTag[Option[String]](), MyTypeTag[Option[Boolean]]())
and see what that gives you, and what you can and can't do with it), so the answer to the question as you ask it is
def getTypeTags[TT <: Product](implicit tt: TypeTag[TT]): List[MyTypeTag[_]] = {
tt.tpe.typeParams.map(_ => MyTypeTag[Option[_]]())
}
You can get the type parameters with tt.tpe.typeParams
, but since that's a runtime value, you can't recover that as a compile-time type T
for MyTypeTag[T]
since it doesn't exist at compile-time yet.
Maybe you can leverage shapeless to do whatever it is you want to do, it has ways to abstract over tuples. See https://underscore.io/books/shapeless-guide/
Upvotes: 0