I am trying to materialize an instance of the (simplified) trait
trait TC[F[_]] {
def apply[A](fa: F[A]): F[A]
}
using Scala macros. The signature of the macro therefore is
def materialize[F[_]](c: Context)(
implicit fT: c.WeakTypeTag[F[_]]): c.Expr[TC[F]]
Type constructor F[_]
now needs to be applied to the type parameter A
for two reasons:
apply
above for a particular F
(like Foo[A]
)Foo[A]
in order to specify an interesting body of apply
Is there any way to create the type corresponding to the method type parameter A
that can than be used in appliedType
? It appears difficult for me, since the method apply
and its type parameter A
are also just being generated as trees.
I tried to take WeakTypeTag[TC[F]]
as additional argument to the macro call and received the paramter type by
val paramT = wfg.tpe.member("apply": TermName).tpe.typeParams.head.tpe
but then using paramT
in q"... def apply[$paramT] ..."
does result in
java.lang.IllegalArgumentException: can't splice "A" as type parameter
so this appears also to be no solution.
I solved the problem by changing the definition of the above trait to
trait TC[F[_]] {
type ApplyF[A] = F[A]
def apply[A](fa: ApplyF[A]): ApplyF[A]
}
and typechecking the tree for a dummy value:
typecheck(q"""new TC[Foo] {
def apply[A](fa: ApplyF[A]): ApplyF[A] = ???
}""").tpe
The typechecked result then could be destructed and transformed (by means of tree transformers) to fill in the ???
. This did not solve the problem completely, yielding the type error:
found : A(in method apply)(in method apply)(in method apply)...
required: A(in method apply)(in method apply)(in method apply)...
While calling untypecheck
before returning the tree did not help - inspection of the resulting tree however showed that the expected result is valid (type correct) Scala code. Thus, the last step that made the macro finally fly was to call
parse(showCode(result))
It feels completely unnecessary, but it seems that this was the only way to get rid of conflicting type information.