case class Fubar(i: Int)
Option(1).map(Fubar.apply)
val tpe = c.weakTypeOf[Test.Fubar]
q"""Option(1).map($tpe.apply)"""
import scala.reflect.runtime.{universe => ru}
import ru._
object Test {
type Context = scala.reflect.macros.blackbox.Context
case class Fubar(i: Int)
def dumb: Option[Test.Fubar] = macro dumbImpl
def dumbImpl(c: Context): c.Expr[Option[Test.Fubar]] = {
import c.universe._
val expr = q"""Option(1).map(Test.Fubar.apply)"""
c.Expr[Option[Test.Fubar]]{expr}
}
def aaaaarrrrrgggggh: Option[Test.Fubar] = macro arghImpl
def arghImpl(c: Context): c.Expr[Option[Test.Fubar]] = {
import c.universe._
val tpe = c.weakTypeOf[Fubar]
val expr = q"""Option(1).map($tpe.apply)"""
c.Expr[Option[Test.Fubar]]{expr}
}
}
scala> :load test.scala...scala> Test.dumbperforming macro expansion $line93.$read.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.Test.dumb at source-<console>,line-56,offset=1229Option(1).map(Test.Fubar.apply)Apply(Select(Apply(Ident(TermName("Option")), List(Literal(Constant(1)))), TermName("map")), List(Select(Select(Ident(TermName("Test")), TermName("Fubar")), TermName("apply"))))res16: Option[Test.Fubar] = Some(Fubar(1))scala> Test.aaaaarrrrrggggghperforming macro expansion $line93.$read.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.Test.aaaaarrrrrgggggh at source-<console>,line-56,offset=1229Option(1).map($line93.$read.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.$iw.Test.Fubar.apply)Apply(Select(Apply(Ident(TermName("Option")), List(Literal(Constant(1)))), TermName("map")), List(Select(TypeTree(), TermName("apply"))))<console>:56: error: value apply is not a member of Test.FubarTest.aaaaarrrrrgggggh^scala>
val companion = t.companion.typeSymbol
q"""Option(1).map($companion.apply)"""
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
case class Fubar(i: Int)
Option(1).map(Fubar.apply(_))
val tpe = c.weakTypeOf[Test.Fubar]
q"""Option(1).map($tpe.apply(_))"""
val tpe = c.weakTypeOf[Test.Fubar.type]
q"""Option(1).map($tpe.apply(_))"""
def myMacro[T: c.weakTypeTag](c: Context): c.Expr[Option[T]] = {
val tpe = c.weakTypeOf[T]
...
}
c.weakTypeOf[T.type]
q"""Option(1).map(${TermName(tpe.toString)}.apply)"""Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_79).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> object Test { case class A(i: Int) }
defined object Test
scala> showRaw(q"Test.A.apply(1)")
res0: String = Apply(Select(Select(Ident(TermName("Test")), TermName("A")), TermName("apply")), List(Literal(Constant(1))))
scala> val tpe = typeOf[Test.A]
tpe: reflect.runtime.universe.Type = Test.A
scala> showRaw(q"$tpe.apply(1)")
res1: String = Apply(Select(TypeTree(), TermName("apply")), List(Literal(Constant(1))))
As you can see the first AST correctly builds the Select, but the second fails to work. The key idea here is that the "Test.A.apply" is forming a Select and quasiquotes isn't smart enough to figure out we want to interpolate that type into that Select. Also I'm not smart enough to know what quasiquotes is doing instead here.
scala> showRaw(q"${tpe.companion}.apply(1)")
res2: String = Apply(Select(TypeTree(), TermName("apply")), List(Literal(Constant(1))))
scala> showRaw(q"${tpe.companion.typeSymbol}.apply(1)")
res3: String = Apply(Select(Ident(Test.A), TermName("apply")), List(Literal(Constant(1))))
scala> showRaw(q"import Test._; ${tpe.typeSymbol.name.toTermName}.apply(1)")
res4: String = Block(List(Import(Ident(TermName("Test")), List(ImportSelector(termNames.WILDCARD, -1, null, -1)))), Apply(Select(Ident(TermName("A")), TermName("apply")), List(Literal(Constant(1)))))
This requires knowing ahead of time what needs to be imported though. After poking around for 5 mins (extent of my attention span) I couldn't find anything to build a Select with the full owner path from a Symbol. So I wrote you a quick and dirty function:
def selectFullTermName(c:Context)(sym: c.Symbol) : c.Tree = {
import c.universe._ sym.fullName.split('.').toList match { case Nil => throw new RuntimeException("unreachable") case head :: tail => tail.foldLeft(Ident(TermName(head)).asInstanceOf[c.Tree]) { case (qualifier,next) => Select(qualifier,TermName(next)) } }}
Now to get the Select built right, we have to start with what we are ultimately trying to select which is the apply method:
val tpe = c.weakTypeOf[Fubar]
val applyMethod = tpe.companion.member(TermName("apply"))
val expr = q"""Option(1).map(${selectFullTermName(c)(applyMethod)})"""
I've attached updated test.scala as well. I'm hoping someone else has prettier answer =]
Enjoy,
Lance