0

With Scala's quasiquotes you can build trees of selects easily, like so:

> tq"a.b.MyObj"
res: Select(Select(Ident(TermName("a")), TermName("b")), TermName("MyObj"))

My question is, how do I do this if the list of things to select from (a,b,...,etc) is variable length (and therefore in a variable that needs to be spliced in)?

I was hoping lifting would work (e.g. tq"""..${List("a","b","MyObj")}""" but it doesn't. Or maybe even this tq"""${List("a","b","MyObj").mkString(".")}""", but no luck.

Is there a way to support this with quasiquotes? Or do I just need to construct the tree of selects manually in this case?

Aish
  • 97
  • 1
  • 8

1 Answers1

1

I don't think there is a way to do this outright with quasiquotes. I'm definitely sure that anything like tq"""${List("a","b","MyObj").mkString(".")}""" will not work. My understanding of quasiquotes is that they are just sugar for extractors and apply.

However, building on that idea, we can define a custom extractor to do what you want. (By the way, I'm sure there is a nicer way to express this, but you get the idea...)

object SelectTermList {
  def apply(arg0: String, args: List[String]): universe.Tree =
    args.foldLeft(Ident(TermName(arg0)).asInstanceOf[universe.Tree])
                 ((s,arg) => Select(s, TermName(arg)))

  def unapply(t: universe.Tree): Option[(String,List[String])] = t match {
    case Ident(TermName(arg0)) => Some((arg0, List()))
    case Select(SelectTermList(arg0,args),TermName(arg)) =>
      Some((arg0, args ++ List(arg)))
    case _ => None
  }
}

Then, you can use this to both construct and extract expressions of the form a.b.MyObj.

Extractor tests:

scala> val SelectTermList(obj0,selectors0) = q"a.b.c.d.e.f.g.h"
obj0: String = a
selectors0: List[String] = List(b, c, d, e, f, g, h)

scala> val q"someObject.method(${SelectTermList(obj1,selectors1)})" = q"someObject.method(a.b.MyObj)"
obj1: String = a
selectors1: List[String] = List(b, MyObj)

Corresponding apply tests:

scala> SelectTermList(obj0,selectors0)
res: universe.Tree = a.b.c.d.e.f.g.h

scala> q"someObject.method(${SelectTermList(obj1,selectors1)})"
res: universe.Tree = someObject.method(a.b.MyObj)

As you can see, there is no problem with nesting the extractors deep inside quasi quotes, both when constructing and extracting.

Alec
  • 31,829
  • 7
  • 67
  • 114