Polymerase Polymerase - 2 months ago 11
Scala Question

Quasiquote interpret tree content instead of taking as literal

Can you please explain why the two usages of Scala quasiquote below give different output between

result1
and
result2
? Is it possible to reproduce
result3
using quasiquote? i.e parse a string content and evaluate it?

import scala.tools.reflect.ToolBox
import scala.reflect.runtime.universe._

val miniSrc = "val lst = (1 to 5).toList ; val sum = lst.foldLeft(0)(_ + _); sum"

val tree1 = q"$miniSrc"
//tree1: reflect.runtime.universe.Tree = "val lst = (1 to 5).toList ; val sum = lst.foldLeft(0)(_ + _); sum"

val tree2 = q"val lst = (1 to 5).toList ; val sum = lst.foldLeft(0)(_ + _); sum"
//tree2: reflect.runtime.universe.Tree =
//{
// val lst = 1.to(5).toList;
// val sum = lst.foldLeft(0)(((x$1, x$2) => x$1.$plus(x$2)));
// sum
//}

val tb = scala.reflect.runtime.currentMirror.mkToolBox()
val result1 = tb.eval(tree1)
//result1: Any = val lst = (1 to 5).toList ; val sum = lst.foldLeft(0)(_ + _); sum

val result2 = tb.eval(tree2)
//result2: Any = 15

val result3 = tb.eval(tb.parse(miniSrc))
//result3: Any = 15

m-z m-z
Answer

Can you please explain why the two usages of Scala quasiquote below give different output between result1 and result2?

miniSrc is a literal String and not a Tree. q"{$miniSrc}" lifts miniSrc, a literal String into another Tree. Lifting does not parse arbitrary code into a Tree, it simply splices together trees or other types into trees. tree1 is therefore a Tree that contains a literal String.

This example ought to illustrate well enough why lifting a literal string into a tree shouldn't involve any parsing:

scala> val miniSrc = "abc"
miniSrc: String = abc

scala> val tree1 = q"$miniSrc"
tree1: reflect.runtime.universe.Tree = "abc"

tree2 is inherently different because you are creating the Tree directly with the quasiquotes interpolator. Therefore, result1 is just a literal string, but result2 is the result of some executed code within tree2.

Is it possible to reproduce result3 using quasiquotes? i.e parse a string content and evaluate it?

No, that's what parsing is for. If you want to lift arbitrary code as a string literal into quasiquotes, you must parse it into a Tree first. Otherwise, it will just be a literal.

scala> val tree1 = q"${tb.parse(miniSrc)}"
tree1: tb.u.Tree =
{
  val lst = 1.to(5).toList;
  val sum = lst.foldLeft(0)(((x$1, x$2) => x$1.$plus(x$2)));
  sum
}

Not that when working with macros, you can parse using the macro's Context. That is, c.parse (instead of using ToolBox).