I have a method taking an HList
and using it to build an instance of a class.
I would like to provide some simplified syntax, hiding the explicit cons. So I'd like to go from:
MyThingy.describe( 42 :: true :: "string" :: HNil)
to
MyThingy.describe {
42
true
"string"
}
where MyThingy
is defined like
class MyThingy[L <: HList](elems: L)
I've made an attempt with this macro
def describe[L <: HList](elements: Unit): MyThingy[L] = macro MyThingyMacros.describeImpl[L]
and
def describeImpl[L <: shapeless.HList : c.WeakTypeTag](c: Context)(elems: c.Tree): c.Tree = {
import c.universe._
def concatHList: PartialFunction[Tree, Tree] = {
case Block(l, _) =>
val els = l.reduceRight((x, y) => q"shapeless.::($x,$y)")
q"$els :: shapeless.HNil"
}
concatHList.lift(elems) match {
case None => c.abort(c.enclosingPosition, "BOOM!")
case Some(elemsHList) =>
val tpe = c.typecheck(elemsHList).tpe
q"new MyThingy[$tpe]($elemsHList)"
}
}
but the typechecker explodes:
exception during macro expansion: scala.reflect.macros.TypecheckException: inferred type arguments [Int,Boolean] do not conform to method apply's type parameter bounds [H,T <: shapeless.HList]
Apparently the compiler is trying to infer [Int, Boolean]
from the block before the macro expansion. I also don't understand why it requires two parameters, where describe
and MyThing
only require one.
Is there a way to have type inference driven by the tree produced by the macro?
If you can live with a comma separated argument list then you could follow the style used in shapeless's HList
companion object apply
method,
scala> import shapeless._
import shapeless._
scala> object MyThingy {
| def describe[P <: Product, L <: HList](p : P)
| (implicit gen: Generic.Aux[P, L]) : L = gen.to(p)
| }
defined object MyThingy
scala> MyThingy.describe(42, true, "String")
res0: this.Repr = 42 :: true :: String :: HNil
scala> res0.head
res1: Int = 42
In general my recommendation is to avoid macros if there is a viable non-macro alternative.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With