I'm going to respectfully disagree with Miles here. I personally canβt tolerate automatic drag and drop, and if you want to use -Xlint in your project, the solution in his answer will cause a lot of warning noise. I definitely agree that you should avoid macros when there is a viable alternative, but if I had to choose between auto-tupling and a macro in the case where I just provide syntactic sugar, I would go with the macro.
In your case, this is not too complicated - there is only a minor error (well, two, really) in your logic. The following will work just fine:
import scala.language.experimental.macros import scala.reflect.macros.whitebox.Context import shapeless._ class MyThingy[L <: HList](val elems: L) def describeImpl[L <: HList: c.WeakTypeTag](c: Context)(elems: c.Tree) = { import c.universe._ def concatHList: PartialFunction[Tree, Tree] = { case Block(statements, last) => statements.foldRight(q"$last :: shapeless.HNil")( (h, t) => q"shapeless.::($h, $t)" ) } 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)" } } def describe[L <: HList](elems: Any): MyThingy[L] = macro describeImpl[L]
Or more briefly:
def describeImpl[L <: HList: c.WeakTypeTag](c: Context)(elems: c.Tree) = { import c.universe._ elems match { case q"{ ..$elems }" => val hlist = elems.foldRight[c.Tree](q"shapeless.HNil: shapeless.HNil")( (h, t) => q"shapeless.::($h, $t)" ) q"new MyThingy($hlist)" case _ => c.abort(c.enclosingPosition, "BOOM!") } }
The biggest problem was only to reduce it - you need to start with HNil , and not create a meaningless intermediate thing, and then apply it. You also need to grab the block expression and enter it as Any instead of Unit to avoid deviating the value.
(As a side note, I'm a little surprised that this works like a white box macro, but as of 2.11.2.)
I personally prefer this syntax with commas, and it is also pretty easy:
def describeImpl[L <: HList: c.WeakTypeTag](c: Context)(elems: c.Tree*) = { import c.universe._ val hlist = elems.foldRight[c.Tree](q"shapeless.HNil: shapeless.HNil")( (h, t) => q"shapeless.::($h, $t)" ) q"new MyThingy($hlist)" } def describe[L <: HList](elems: Any*): MyThingy[L] = macro describeImpl[L]
The use here is the same as with the solution of the product, but there is no automatic tupling involved.