Both are happening at the same time in both examples.
Unquoting is the process of substituting a Tree
somewhere into the structure of another Tree
(like interpolating). In this example, ints
isn't exactly a Tree
, but there exists a Liftable[List[T]]
that allows us to unquote a List[T]
into a Tree
, as if it were a Tree
(ie. the Liftable
tells the compiler how to transform the literal List[Int]
here into a Tree
so that it may be substituted).
To quote the documentation:
Unquote splicing is a way to unquote a variable number of elements.
Here, the variable number of elements would be the elements in the List
we want to unquote. If we did q"f($ints)"
, then we would simply be unquoting ints
as a single argument of f
. But perhaps we want to apply repeated parameters to f
instead. For that we use unquote splicing.
q"f(..$ints) // Using `..` means we get f(1, 2, 3) instead of f(List(1, 2, 3))
Again, the documentation says it best, really:
Dots near unquotee annotate degree of flattening and are also called splicing rank. ..$
expects argument to be an Iterable[Tree]
and ...$
expects Iterable[Iterable[Tree]]
.
So lifting allows us to unquote a List[T]
into the tree f(x)
as if it were an Iterable[Tree]
, and unquote splicing allows us to unquote the variable number of elements the List[T]
contained as multiple arguments for f
.
Here are the different relevant combinations:
val listTree = q"scala.collection.immutable.List(1, 2, 3)"
val treeList = List(q"1", q"2", q"3")
val literalList = List(1, 2, 3)
scala> q"f($listTree)" // plain unquoting from another Tree
res6: reflect.runtime.universe.Tree = f(scala.collection.immutable.List(1, 2, 3))
scala> q"f($literalList)" // unquoting from lifting
res7: reflect.runtime.universe.Tree = f(scala.collection.immutable.List(1, 2, 3))
scala> q"f(..$treeList)" // plain unquote splicing
res8: reflect.runtime.universe.Tree = f(1, 2, 3)
scala> q"f(..$literalList)" // unquote splicing and lifting
res9: reflect.runtime.universe.Tree = f(1, 2, 3)