QuasiQuotes and Literals

375 views
Skip to first unread message

Nathan Matthews

unread,
Feb 7, 2014, 12:21:44 PM2/7/14
to scala-l...@googlegroups.com
Hi All,

I have a scenario where my macro has generated a value. Lets say its a Map. I want the literal map to be transformed into an AST for insertion into the generated code. Is there an easy way to do this with QuasiQuotes? 

E.g.

In macro...

val map : Map[String, Int] = someFunctionThatGeneratesAMap(context)

val tree : c.Tree = toTree(map) // returns something like: q"map("the" -> 0, "answer" -> 42)"

Jason Zaugg

unread,
Feb 7, 2014, 12:27:25 PM2/7/14
to scala-l...@googlegroups.com
On Fri, Feb 7, 2014 at 6:21 PM, Nathan Matthews <nathan.r...@gmail.com> wrote:
Hi All,

I have a scenario where my macro has generated a value. Lets say its a Map. I want the literal map to be transformed into an AST for insertion into the generated code. Is there an easy way to do this with QuasiQuotes? 

That's the perfect use case for Liftable [1]. One of the standard liftable instances [2] handles maps (assuming we can also find liftable instances for the key and value types):

scala> val m = Map(1 -> "one"); q"$m"
m: scala.collection.immutable.Map[Int,String] = Map(1 -> one)
res1: $r.intp.global.Tree = scala.collection.immutable.Map(scala.Tuple2(1, "one"))

-jason
 

E.g.

In macro...

val map : Map[String, Int] = someFunctionThatGeneratesAMap(context)

val tree : c.Tree = toTree(map) // returns something like: q"map("the" -> 0, "answer" -> 42)"

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Nathan Matthews

unread,
Feb 7, 2014, 6:16:29 PM2/7/14
to scala-l...@googlegroups.com
Hi Jason,

Thanks for the reply, I'm having some issues using Liftable. I'm on Scala 2.10.3. Standard Liftables doesn't seem to be available for that release so i'm trying to roll my own. The type signature for liftable is:

trait Liftable[T] {
  def apply(universe: Universe, value: T): universe.Tree
}

But in the context of the macro I have a different universe: context.universe. Im not sure how I'd go about creating a Liftable which lifts context.universe.Type for instance. I could call universe.TypeTree(value) but value has to be of type universe.Type. I'm not sure how I'd constrain the type parameter T on the Liftable class to be the path dependent type universe.Type in that instance. I must be missing some magic.

- Nathan

Denys Shabalin

unread,
Feb 8, 2014, 7:05:05 AM2/8/14
to scala-l...@googlegroups.com
Hi Nathan,

Unfortunately 2.10.x paradise doesn't contain latest liftables and newer simpler api that will be a part of 2.11 release.

In 2.10 you could do something like:

import universe1._

case class Foo(typ: Type)

implicit val liftFoo: Liftable[Foo] = new Liftable[Foo] {
   def apply(universe2, foo): Tree = {
      require(universe1 == universe2)
      import universe2._
      val typ2  = foo.type.asInstanceOf[univese2.Type]     
      // lifting code goes here
   }
}

Eugene Burmako

unread,
Feb 8, 2014, 7:08:14 AM2/8/14
to scala-l...@googlegroups.com
Just to add to Denys's excellent answer, we're planning to eventually migrate 2.11 stuff to 2.10 paradise right before or shortly after the 2.11.0-final release.

Naftoli Gugenheim

unread,
Feb 12, 2014, 12:17:02 AM2/12/14
to scala-l...@googlegroups.com, Alexander Nemish

Great to hear about Liftable. I could have used it some time ago...

Nick Stanchenko

unread,
Mar 16, 2014, 2:25:26 PM3/16/14
to scala-l...@googlegroups.com
Hi,

I’m having trouble implementing even a simple Liftable due to some path-dependent type weirdness. I’m on 2.10.3 with quasiquotes-M3.

object X {
  implicit def `List is liftable`[A: Liftable] = new Liftable[List[A]] {
    def apply(universe: Universe, value: List[A]) = {
      import universe._
      q"scala.collection.immutable.List(..$value)"
    }
  }
}

type mismatch;
[error]  found   : universe.Apply
[error]  required: universe.Tree

The cast does not help:

object X {
  implicit def `List is liftable`[A: Liftable] = new Liftable[List[A]] {
    def apply(universe: Universe, value: List[A]) = {
      import universe._
      q"scala.collection.immutable.List(..$value)".asInstanceOf[universe.Tree]
    }
  }
}

type mismatch;
[error]  found   : universe.Tree
[error]  required: universe.Tree

Nick

Eugene Burmako

unread,
Mar 17, 2014, 6:02:16 PM3/17/14
to scala-l...@googlegroups.com
Looks like you need to specify the return type of apply explicitly. No idea why this is required :(


--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Nick Stanchenko

unread,
Mar 17, 2014, 6:07:03 PM3/17/14
to scala-l...@googlegroups.com
That works! Interesting...

While we are at it, what’s the closest I can get to `symbolOf` on 2.10?

Eugene Burmako

unread,
Mar 17, 2014, 6:07:31 PM3/17/14
to scala-l...@googlegroups.com
I guess that would be typeOf[T].typeSymbol

Nick Stanchenko

unread,
Mar 17, 2014, 6:12:58 PM3/17/14
to scala-l...@googlegroups.com
Thanks! Also, is there a simple way to get the fully qualified method symbol for a singleton method? Or should I use `q"${typeOf[MySingleton.type]`.typeSymbol}.meth` (don’t know if that works)? I’ve just got tired of qualifying everything by hand and want to learn the right way :)

Eugene Burmako

unread,
Mar 17, 2014, 6:20:58 PM3/17/14
to scala-l...@googlegroups.com
I think that's the best you can get at the moment, while quasiquotes are still unhygienic. It also helps to predefine a bunch of vals that refer to frequently used symbols.

Nick Stanchenko

unread,
Mar 17, 2014, 8:06:57 PM3/17/14
to scala-l...@googlegroups.com
Hmm...

implicitly[Liftable[List[Tree]]]
// compiles

def test(list: List[Tree]) = q"$list"
Can't splice List[....universe.Tree], consider using ..
[error]     def apply(list: List[Tree]) = q"$list"
[error]                                      ^

So here I am, really trying to use Liftable and stuff...

Eugene Burmako

unread,
Mar 17, 2014, 8:11:37 PM3/17/14
to scala-l...@googlegroups.com, Denys Shabalin
Quite possibly this is something not yet supported in 2.10's implementation of quasiquotes. Denys might know more.

Nick Stanchenko

unread,
Mar 17, 2014, 8:21:30 PM3/17/14
to scala-l...@googlegroups.com, Denys Shabalin
> Quite possibly this is something not yet supported in 2.10's implementation of quasiquotes. Denys might know more.

Well, the docs kind of hint that it should be (http://docs.scala-lang.org/overviews/macros/quasiquotes.html#tips_and_tricks), but do not give any information (unlike the awesome 2.11 guide). I read that bit quite a while ago and all this time was under the impression that it was the way to go :)

Also, I haven’t checked whether it’s something implemented in 2.11 yet, but would be nice to have a macro-materialized Liftable instance for case classes. I think paradise/palladium committers have a much higher chance of doing it properly and it would save a lot of effort for those who want to unquote a variety of case classes.

Alex Cruise

unread,
Mar 17, 2014, 9:02:27 PM3/17/14
to scala-l...@googlegroups.com, Denys Shabalin
I know I still sometimes get tripped up by this... I don't know if it was causing problems for you specifically, but...


implicit def `List is liftable`[A: Liftable] = new Liftable[List[A]] {

There's no explicit return type on this implicit, which limits its accessbility to callsites that are syntactically afterward, in the same file.  At least it used to, someone might have fixed that. :)

-0xe1a
 


--

Nick Stanchenko

unread,
Mar 17, 2014, 9:13:37 PM3/17/14
to scala-l...@googlegroups.com, Denys Shabalin
Alex, good point! Normally I try to follow the practice of providing explicit return types for implicits, but for some reason did not do it here.

However this brought even more weirdness!

// Without explicit type

implicit object `X is liftable` extends Liftable[X] { ... }
implicit def `List is liftable`[A: Liftable] = new Liftable[List[A]] { ... }
implicitly[Liftable[List[X]]] // OK

// With explicit type

implicit def `List is liftable`[A: Liftable]: Liftable[List[A]] = new Liftable[List[A]] {
    def apply(universe: Universe, value: List[A]): universe.Tree = {
      import universe._
      q"scala.collection.immutable.List(..$value)"
    }
  }

overloaded method value Apply with alternatives:
[error]   (sym: universe.Symbol,args: universe.Tree*)universe.Tree <and>
[error]   => universe.ApplyExtractor
[error]  cannot be applied to (universe.Select, universe.Tree)
[error]       q"scala.collection.immutable.List(..$value)"
[error]       ^

Eugene Burmako

unread,
Mar 18, 2014, 5:40:11 AM3/18/14
to scala-l...@googlegroups.com, Denys Shabalin
I believe this could be of interest to you: https://github.com/scala/scala/pull/3326


--

Eugene Burmako

unread,
Mar 18, 2014, 5:43:06 AM3/18/14
to scala-l...@googlegroups.com, Denys Shabalin
It looks like this implicit picks itself inside the quasiquote, because quasiquotes see ..$ and that somehow triggers a lookup for List[A]. Denys could provide the ultimate reference here, but I think we should just wait until 2.11 quasiquotes are backported to 2.10 paradise (because there was a number of changes to lifting recently) and maybe then the problem will fix itself.


--

Nick Stanchenko

unread,
Mar 18, 2014, 11:42:16 AM3/18/14
to scala-l...@googlegroups.com, Denys Shabalin
I believe this could be of interest to you: https://github.com/scala/scala/pull/3326
Ah, yes. Funny enough, now I even remember reading that pull request :) Sorry then!


It looks like this implicit picks itself inside the quasiquote, because quasiquotes see ..$ and that somehow triggers a lookup for List[A]. Denys could provide the ultimate reference here, but I think we should just wait until 2.11 quasiquotes are backported to 2.10 paradise (because there was a number of changes to lifting recently) and maybe then the problem will fix itself.
I thought that might be a recursion problem, but wasn’t the compiler supposed to complain about the unspecified return type? 
Anyway, I agree that we should wait for backporting. Do you have any estimates for when that could happen?

Nick Stanchenko

unread,
Mar 23, 2014, 12:30:24 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
That was fast! I tried the new backport (M4) and it compiles. However at runtime I get

exception during macro expansion: 
[error] java.lang.ClassNotFoundException: scala.reflect.api.Universe$Liftable

Is there a way to fix this?

Eugene Burmako

unread,
Mar 23, 2014, 12:34:39 PM3/23/14
to scala-l...@googlegroups.com
Do you have the quasiquote lib on your classpath?
--

Eugene Burmako

unread,
Mar 23, 2014, 12:36:50 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
Oh man I know. I had to comment out java files because scaladoc was failing on them, but in doing so I made sbt produce a jar without those files compiled. It looks like I blew a release. Will publish M5 within the hour...

On 23 Mar 2014, at 17:30, Nick Stanchenko <nick....@gmail.com> wrote:

--

Nick Stanchenko

unread,
Mar 23, 2014, 12:39:40 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
Nice :) I was already suspicious when IDEA rendered `Liftable` in red...

Eugene Burmako

unread,
Mar 23, 2014, 1:07:32 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
For that matter, I don't think that IDEA is going to support liftables in 2.10.x, since they are introduced by a compiler plugin hack. However, the crash you're facing can and shall be fixed.

Nick Stanchenko

unread,
Mar 23, 2014, 1:23:50 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
Right. We already submitted one issue like this (http://youtrack.jetbrains.com/issue/SCL-5965) and apparently they want to focus on 2.11 & Palladium instead, so not worth bugging them again.

Eugene Burmako

unread,
Mar 23, 2014, 1:55:19 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
I've just released 2.0.0-M5 and that should take care of the issue.


--

Nick Stanchenko

unread,
Mar 23, 2014, 1:59:32 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
I got

exception during macro expansion: 
[error] java.lang.UnsupportedClassVersionError: scala/reflect/api/Universe$Liftable : Unsupported major.minor version 51.0

Something on my side?

Nick Stanchenko

unread,
Mar 23, 2014, 2:03:00 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin

Eugene Burmako

unread,
Mar 23, 2014, 2:02:51 PM3/23/14
to scala-l...@googlegroups.com, Denys Shabalin
It's fascinating how many facets fail can have! This one is caused by Java-compiled portions of quasiquotes.jar being compiled with Java 7 :)

Would it be possible for you to use Java 7 or 8? I'd prefer to cool down the pace of releases if that's not an absolute blocker for you. Alternatively I could publish a snapshot build with Java 6.
Reply all
Reply to author
Forward
0 new messages