scalareflectionscala-quasiquotes

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

Solution

  • 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).