scala - type parameter mismatch with WeakTypeTag reflection + quasiquoting (I think!) -
inspired travisbrown, i'm trying use macro create "smart constructors".
given
package mypkg sealed trait hello[a] case class ohayo[a,b](a: (a,b)) extends hello[a]
and
val smartconstructors = freemacros.liftconstructors[hello]
the macro should find subclasses of hello
, @ constructors, , extract few elements populate tree "smart constructor":
q""" def $methodname[..$typeparams](...$paramlists): $basetype = $companionsymbol[..$typeargs](...$arglists) """
i hoped get:
val smartconstructors = new { def ohayo[a, b](a: (a, b)): hello[a] = ohayo[a, b](a) }
but instead get:
error: type mismatch; found : (a(in class ohayo), b(in class ohayo)) required: ((some other)a(in class ohayo), (some other)b(in class ohayo)) val liftedconstructors = freemacros.liftconstructors[hello]
at glance, tree looks ok me:
scala> q" new { ..$welltyped }" res1: u.tree = { final class $anon extends scala.anyref { def <init>() = { super.<init>(); () }; def ohayo[a, b](a: (a, b)): net.arya.constructors.hello[a] = ohayo[a, b](a) }; new $anon() }
but guess invisibly isn't. if naively try freshen typeparams info.typeparams.map(p => typename(p.name.tostring))
, "can't splice type parameter" when quasiquoting.
where going wrong? taking look.
-arya
import scala.language.experimental.macros import scala.reflect.api.universe import scala.reflect.macros.whitebox class freemacros(val c: whitebox.context) { import c.universe._ import freemacros._ def liftedimpl[f[_]](implicit t: c.weaktypetag[f[_]]): tree = { val atc = t.tpe val childsymbols: set[classsymbol] = subcaseclasssymbols(c.universe)(atc.typesymbol.asclass) val welltyped = childsymbols.map(ctorsforsymbol(c.universe)(atc)).unzip q"new { ..${welltyped} }" } } object freemacros { def liftconstructors[f[_]]: = macro freemacros.liftedimpl[f] def smartname(name: string): string = ( name.tolist match { case h :: t => h.tolower :: t case nil => nil } ).mkstring def subcaseclasssymbols(u: universe)(root: u.classsymbol): set[u.classsymbol] = { val subclasses = root.knowndirectsubclasses val cast = subclasses.map(_.asinstanceof[u.classsymbol]) val partitioned = mapped.partition(_.iscaseclass) partitioned match { case (caseclasses, regularclasses) => caseclasses ++ regularclasses.flatmap(r => subcaseclasssymbols(u)(r)) } } def ctorsforsymbol(u: universe)(atc: u.type)(caseclass: u.classsymbol): (u.defdef, u.defdef) = { import u._ import internal._ // these didn't // def cleartypesymbol(s: symbol): typesymbol = internal.newtypesymbol(nosymbol, s.name.totypename, s.pos, if(s.isimplicit)flag.implicit else noflags) // def cleartypesymbol2(s: symbol): typesymbol = internal.newtypesymbol(nosymbol, s.name.totypename, noposition, if(s.isimplicit)flag.implicit else noflags) // def cleartypedef(d: typedef): typedef = internal.typedef(cleartypesymbol(d.symbol)) val companionsymbol: symbol = caseclass.companion val info: type = caseclass.info val primaryctor: symbol = caseclass.primaryconstructor val method = primaryctor.asmethod val typeparams = info.typeparams.map(internal.typedef(_)) // val typeparams = info.typeparams.map(s => typedef(newtypesymbol(nosymbol, s.name.totypename, noposition, noflags))) // val typeparams = info.typeparams.map(s => internal.typedef(cleartypesymbol2(s))) val typeargs = info.typeparams.map(_.name) val paramlists = method.paramlists.map(_.map(internal.valdef(_))) val arglists = method.paramlists.map(_.map(_.asterm.name)) val basetype = info.basetype(atc.typesymbol) val list(returntype) = basetype.typeargs val methodname = termname(smartname(caseclass.name.tostring)) val welltyped = q""" def $methodname[..$typeparams](...$paramlists): $basetype = $companionsymbol[..$typeargs](...$arglists) """ welltyped } }
p.s. have been experimenting toolbox.untypecheck / typecheck per this article haven't found working combination.
you need using
clas.typeargs.map(_.tostring).map(name => { typedef(modifiers(flag.param),typename(name), list(),typeboundstree(emptytree, emptytree)) }
replace info.typeparams.map(p => typename(p.name.tostring))
it si code
object getsealedsubclass { def ol3[t]: = macro getsealedsubclassimpl.ol3[t] } class getsealedsubclassimpl(val c: context) { import c.universe._ def showinfo(s: string) = c.info(c.enclosingposition, s.split("\n").mkstring("\n |---macro info---\n |", "\n |", ""), true) def ol3[t: c.weaktypetag]: c.universe.tree = { //get sub class val subclass = c.weaktypeof[t] .typesymbol.asclass.knowndirectsubclasses .map(e => e.asclass.totype) //check type params must ia s sealed class if (subclass.size < 1) c.abort(c.enclosingposition, s"${c.weaktypeof[t]} not sealed class") // sub class constructor params val subconstructorparams = subclass.map { e => //get constructor e.members.filter(_.isconstructor) //if class has many constructor need filter main constructor .head.map(s => s.asmethod) //get function param list }.map(_.asmethod.paramlists.head) .map(_.map(e => q"""${e.name.totermname}:${e.info} """)) val outfunc = subclass zip subconstructorparams map { case (clas, parm) => q"def smartconstructors[..${ clas.typeargs.map(_.tostring).map(name => { typedef(modifiers(flag.param), typename(name), list(), typeboundstree(emptytree, emptytree)) }) }](..${parm})=${clas.typesymbol.name.totermname} (..${parm})" } val outclass = q""" object term{ ..${outfunc} } """ showinfo(show(outclass)) q"""{ $outclass term } """ } }
using
sealed trait hello[a] case class ohayo[a, b](a: (a, b)) extends hello[a] object getsealed extends app { val = getsealedsubclass.ol3[hello[_]] val b=a.asinstanceof[ {def smartconstructors[a, b](a: (a, b)): ohayo[a, b]}].smartconstructors(1, 2).a println(b) }
Comments
Post a Comment