Get Name of Reified Function Using Scala Macros - scala

I would like to pattern match certain function and other variable references inside a macro traversing over a Tree. Currently, I am matching based on symbol.fullname like this:
tree match {
case "$f($x)" if f.symbol != null
&& f.symbol.fullName == "_root_.mypackage.MyClass.myfunc" =>
...
case "$f($x)" if f.symbol != null
&& f.symbol.fullName == "_root_.mypackage.MyClass2.myfunc2" =>
...
case ...
}
However, I would like to additionally check during compilation of this macro that those functions actually exists, e.g. instead of having String, I would like to directly use references, such that the IDE can tell me whether I had a typo in the 'myfunc' name. I was thinking of using using reify, but I am not sure whether that works.
Now lets assume i was looking for println instead of MyClass.myfunc. The first problem I encountered is that you cannot reify(println) or any other function reference directly, because in Scala there are no function references, so you have to write reify(println _) or more specificly in this case reify(println (_: String)) to select what println function we want to call. With the following code I collected all symbols that are inside the expression, but sadly only Predef (from Predef.println) is found but not println itself:
println(reify( println (_:String) ).tree
.collect { case x if x.symbol != null => x.symbol.fullName } )
// List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)
Any ideas for getting the name of something in scala (currently using 2.12)?

reify(println) compiles.
How do you imagine symbol of function println (_:String)? Where is this function defined? In Predef two methods are defined:
def println(): Unit
def println(x: Any): Unit
Try
val mirror = scala.reflect.runtime.universe.runtimeMirror(this.getClass.getClassLoader) // at runtime
// val mirror = c.mirror // at compile time
mirror.staticClass("mypackage.MyClass").typeSignature.decl(TermName("myfunc"))
// method myfunc
or
typeOf[MyClass].decl(TermName("myfunc"))
// method myfunc
for MyClass#myfunc
definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
// List(method println, method println)
for both println
definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
.filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
// List(method println)
for println(Any):Unit.
For example
def foo(x: Any): Unit = macro impl
def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
import c.universe._
val printlnSymb = definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
.filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
.head
x match {
case q"$f($x)" if f.symbol == printlnSymb =>
println("test")
}
q"()"
}
foo(println(1)) //Warning:scalac: test
reify( println (_:String) ).tree.collect { ... produces only List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef) because tree reify( println (_:String) ).tree is not typechecked (for typechecked tree it produces List($anonfun, x$1, java.lang.String, scala.Predef.println, scala.Predef.println, scala.Predef, x$1, java.lang.String)).
So another option is to do c.typecheck
def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
import c.universe._
val printlnSymb = (c.typecheck(q"println(_:Any)", silent = false) match {
case q"($_) => $p($_)" => p
}).symbol
//val printlnSymb = (c.typecheck(reify { println(_:Any) }.tree, silent = false) match {
// case q"($_) => $p($_)" => p
//}).symbol
x match {
case q"$f($x)" if f.symbol == printlnSymb =>
println("test")
}
q"()"
}

To get the symbols of a reified expressions, the tree of the expressions needs another explicitly pass through typechecking, so the following code works:
val mySym1 = c.typecheck(reify(mypackage.myfunction1 _).tree) match {
case q"{(..$_) => $f(..$_)}" => f.symbol
}
val mySym2 = c.typecheck(reify(mypackage.myfunction2 _).tree) match {
case q"{(..$_) => $f(..$_)}" => f.symbol
}
println(mySym.fullName)
// _root_.mypackage.myfunction1
Then you can match based on the symbol:
tree match {
case "$f($x)" if f.symbol != null => f.symbol match {
case x if x == mySym1 => ...
case x if x == mySym2 => ...
case ...
}
case ...
}

There is actually a function called def symbolOf[X]: TypeSymbol that can be used to get a TypeSymbol from a Type. But this only works for TypeSymbols, not TermSymbols, so we cannot do this on functions.
However, if this is our own function, we can replace the definition from def myfunc(): Int = ... with object myfunc { def apply(): Int = ... }. As each object has its own type (called myfunc.type) we can now do the following
package main
object myfunc { def apply(): Int = 1 }
object myfunc2 { def apply(): Int = 1 }
...
tree match {
case "$f.apply($x)" if f.symbol == symbolOf[myfunc.type] => ...
case "$f.apply($x)" if f.symbol == symbolOf[myfunc2.type] => ...
case ...
}

Related

How can I consume a Scala macro/quasiquote for code templates?

I want to generate a bunch of objects at compile time that follow a simple pattern, so I wrote the following macro:
object MyMacro {
def readWrite[T](taName: String, readParse: String => T, label: String, format: T => String): Any = macro readWriteImpl[T]
def readWriteImpl[T: c.WeakTypeTag](c: Context)(taName: c.Expr[String], readParse: c.Expr[String => T], label: c.Expr[String], format: c.Expr[T => String]): c.Expr[Any] = {
import c.universe._
def termName(s: c.Expr[String]): TermName = s.tree match {
case Literal(Constant(s: String)) => TermName(s)
case _ => c.abort(c.enclosingPosition, "Not a string literal")
}
c.Expr[Any](q"""
object ${termName(taName)} extends TypeAdapter.=:=[${implicitly[c.WeakTypeTag[T]].tpe}] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): ${implicitly[c.WeakTypeTag[T]].tpe} =
reader.readString(path) match {
case null => null.asInstanceOf[${implicitly[c.WeakTypeTag[T]].tpe}]
case s => Try( $readParse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse "+${termName(label)}+" from input '"+s+"'", List.empty[String], u)
}
}
def write[WIRE](t: ${implicitly[c.WeakTypeTag[T]].tpe}, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString($format(t), out)
}
}
""")
}
}
I'm not sure I have the return value for readWrite and readWriteImpl correct--the compiler complains mightily about some assertion failure!
I'm also not sure how to actually consume this macro. First I tried (in a separate compilation unit):
object TimeFactories {
MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString)
}
Didn't work. If I tried to reference TimeFactories.DurationTypeAdapterFactory I got an error saying it wasn't found. Next I thought I'd try assigning it to a val...didn't work either:
object Foo {
val duration = MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString).asInstanceOf[TypeAdapterFactory]
}
How can I wire this up so I get generated code compiled like this:
object TimeFactories{
object DurationTypeAdapterFactory extends TypeAdapter.=:=[Duration] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): Duration =
reader.readString(path) match {
case null => null.asInstanceOf[Duration]
case s => Try( Duration.parse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse Duration from input 'Duration'", List.empty[String], u)
}
}
def write[WIRE](t: Duration, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString(t.toString, out)
}
}
// ... More invocations of the readWrite macro with other types for T
}
I don't think, that you can generate new identifiers using macros and than use them publicly.
Instead, try to replace object ${termName(taName)} extends TypeAdapter simply with new TypeAdapter and assign invocation of the macro to a val (as in your second example). You will then reference an anonymous (and generated) class stored in a val. Parameter taName becomes redundant.

How to convert context.universe.Annotation to MyAnnotation in a Scala Macro

I am on Scala 2.11.1, I have an annotation
case class MyAnnotation(id: String, message: String) extends StaticAnnotation
I would like to create a macro MessageFromMyAnnotation that transform the following code
class Foo {
#MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = MessageFromMyAnnotation("001")
... // my messy code
}
}
to
class Foo {
#MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = "Hello world, James Bond 001"
... // my messy code
}
}
In brief, the macro find, on its enclosing element, a message of #MyAnnotation whose id = "001" and return "Hello world, " + message
This is the macro
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
c.internal.enclosingOwner.annotations.filter( anno =>
anno.tpe =:= c.universe.typeOf[MyAnnotation] &&
anno.asInstanceOf[MyAnnotation].id == id.value //this does not work
) match {
case anno :: Nil => c.universe.reify("Hello world, " + ...)
case x => c.abort(c.enclosingPosition, c.universe.showRaw(x))
}
}
}
I want to convert anno of type cuniverse.Annotation to MyAnnotation and compare its id with the argument id of type c.Expr[String], but the anno.asInstanceOf[MyAnnotation] yields a ClassCastException and id.value gives me an error message
cannot use value except for signatures of macro implementations
So, please help me with 2 questions:
How to convert anno of type cuniverse.Annotation to MyAnnotation
How to compare its id with the argument id of type c.Expr[String]
I have successfully made it thanks to #Imm's suggestion:
You don't have an instance of MyAnnotation - this is compile time, you only
have an AST that represents the call. You can get the Expr that's the
parameter given to the cuniverse.Annotation, and either splice it, or pattern
match it as a String literal and then take the value out of that.
And here is the code
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
import c.universe._
id match { case Expr(Literal(Constant(idVal: String))) =>
(for { Annotation(tpe,
Literal(Constant(annoIdVal: String)) ::
Literal(Constant(annoMessageVal: String)) ::
Nil, _) <- c.internal.enclosingOwner.annotations
if tpe =:= typeOf[MyAnnotation] && idVal == annoIdVal
} yield (annoIdVal, annoMessageVal)
) match {
case (annoIdVal, annoMessageVal) :: Nil =>
reify(c.literal("Hello world, " + annoMessageVal).splice)
case matchedMore :: thanOne :: Nil => c.abort(c.enclosingPosition, "Found more than one #MyAnnotation with the same id")
case x => c.abort(c.enclosingPosition, "Not Found #MyAnnotation with the specified id")
}
}
}
}

Compiler crash in simple macro manipulating a match statement

def control(x: String): Option[String] = macro controlImpl
def controlImpl(c: Context)(x: c.Expr[String]): c.Expr[Option[String]] = {
import c.universe._
val result = x.tree match {
case Match(expr, cases) =>
val matchz = Match(q"""List("hello")""", cases)
q"Some(List(5)).map{a => $matchz}"
case a => a
}
c.Expr[Option[String]](result)
}
This macro crashes during macro expansion with the following error:
java.lang.IllegalArgumentException: Could not find proxy for val o7: Some in List(value o7, method apply, <$anon: Function1>, value f, method apply, <$anon: Function1>, method apply, <$anon: Function0>, value <local ApplicativeTest>, object ApplicativeTest, package scalaz, package <root>) (currentOwner= value one )
This is the macro application point:
val f = IdiomBracket.control {
a.get match {
case List(one) => one
case _ => ""
}
}
What is odd is that if you substitute q"Some(List(5)).map{a => $matchz}" with q"Some($matchz)" then the compiler crash goes away.
"Simple macro" is an oxymoron.
The crash is in lambda lift, which indicates a bad symbol owner.
You can inspect the tree you produce with -Ybrowse:typer.
Consider what you'd like to produce:
class X {
//def g = List("hello") match { case List(x) => x case _ => "" }
def f = Some(List(5)).map{a => List("hello") match { case List(x) => x case _ => "" } }
}
The var in the case is owned by the anonfun you want to create:
Symbol: [has] value x
Symbol owner: value $anonfun
Your actual result:
Symbol: [has] value x
Symbol owner: value <local Controlled>
where Controlled is my sample enclosing object for your code.
You might have to rebuild the cases, as opposed to just cases map (_.untypecheck).
https://github.com/scala/scala/blob/v2.11.5/src/reflect/scala/reflect/api/Trees.scala#L1100

Scala macro: rewrite partial function into match

I would like to rewrite partial function into match expression.
//macro and impl
def pfRewrite(pf: PartialFunction[Int, Int]) = macro ResponderImpl.pfRewriteImpl
def pfRewriteImpl(c: Context)(pf: c.Expr[PartialFunction[Int, Int]]) = {
import c.universe._
val cas = pf.tree.collect {
case x: DefDef if x.name.decoded == "applyOrElse" =>
x.collect {
case cq"$x => $y" => cq"""$x => $y"""
}
}.flatten
val sVal = newTermName("someVal")
val rewrite = q"""
$sVal match {
case ..$cas
}
"""
c.Expr(rewrite)
}
In code i got a PartialFunction and take a cases from applyOrElse method, next i create a match expression for "someVal". This value from code:
//test
def test {
val someVal = 10
pfRewrite {
case 1 => 10
case _ => 100
}
}
But i got errors:
[error] found : Int(10)
[error] required: Nothing
[error] case 1 => 10
[error] ^
and etc.
It possible rewrite partial function call into match ?
Typically these kinds of awkward error messages stem from mixing typed and untyped trees (for more details take a look at Scala macros: What is the difference between typed (aka typechecked) an untyped Trees). Therefore, until we've fixed the situation, it's good practice to do resetLocalAttrs on typed trees that you plan to mix with untyped ones.
This case is not an exception. Using c.resetLocalAttrs(pf.tree) instead of just pf.tree reveals the real problem with the macro expansion:
12:57 ~/Projects/Master/sandbox (master)$ scalac Test.scala
Test.scala:5: error: not found: value default
Macros.pfRewrite {
^
If we enable -Xprint:typer, which is one of the ways to see macro expansions (along with -Ymacro-debug-lite and -Ymacro-debug-verbose), then we'll see what's going on:
[[syntax trees at end of typer]] // Test.scala
package <empty> {
object Test extends AnyRef with App {
def <init>(): Test.type = {
Test.super.<init>();
()
};
def test: Unit = {
val someVal: Int = 10;
{
someVal match {
case 1 => 10
case _ => 100
case (defaultCase$ # _) => <default: error>.apply(<x1: error>)
};
()
}
}
}
}
It looks like partial function synthesis also adds a default case that we can't reuse as-is, as it refers to some synthetic variables defined in the synthesized class.

ClassTag based pattern matching fails for primitives

I thought the following would be the most concise and correct form to collect elements of a collection which satisfy a given type:
def typeOnly[A](seq: Seq[Any])(implicit tag: reflect.ClassTag[A]): Seq[A] =
seq.collect {
case tag(t) => t
}
But this only works for AnyRef types, not primitives:
typeOnly[String](List(1, 2.3, "foo")) // ok. List(foo)
typeOnly[Double](List(1, 2.3, "foo")) // fail. List()
Obviously the direct form works:
List(1, 2.3, "foo") collect { case d: Double => d } // ok. List(2.3)
So there must be a (simple!) way to fix the above method.
It's boxed in the example, right?
scala> typeOnly[java.lang.Double](vs)
res1: Seq[Double] = List(2.3)
Update: The oracle was suitably cryptic: "boxing is supposed to be invisible, plus or minus". I don't know if this case is plus or minus.
My sense is that it's a bug, because otherwise it's all an empty charade.
More Delphic demurring: "I don't know what the given example is expected to do." Notice that it is not specified, expected by whom.
This is a useful exercise in asking who knows about boxedness, and what are the boxes? It's as though the compiler were a magician working hard to conceal a wire which keeps a playing card suspended in midair, even though everyone watching already knows there has to be a wire.
scala> def f[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect {
| case v if t.runtimeClass.isPrimitive &&
| ScalaRunTime.isAnyVal(v) &&
| v.getClass.getField("TYPE").get(null) == t.runtimeClass =>
| v.asInstanceOf[A]
| case t(x) => x
| }
f: [A](s: Seq[Any])(implicit t: scala.reflect.ClassTag[A])Seq[A]
scala> f[Double](List(1,'a',(),"hi",2.3,4,3.14,(),'b'))
res45: Seq[Double] = List(2.3, 3.14)
ScalaRunTime is not supported API -- the call to isAnyVal is just a match on the types; one could also just check that the "TYPE" field exists or
Try(v.getClass.getField("TYPE").get(null)).map(_ == t.runtimeClass).getOrElse(false)
But to get back to a nice one-liner, you can roll your own ClassTag to handle the specially-cased extractions.
Version for 2.11. This may not be bleeding edge, but it's the recently cauterized edge.
object Test extends App {
implicit class Printable(val s: Any) extends AnyVal {
def print = Console println s.toString
}
import scala.reflect.{ ClassTag, classTag }
import scala.runtime.ScalaRunTime
case class Foo(s: String)
val vs = List(1,'a',(),"hi",2.3,4,Foo("big"),3.14,Foo("small"),(),null,'b',null)
class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] {
override def runtimeClass = t.runtimeClass
/*
override def unapply(x: Any): Option[A] = (
if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) &&
x.getClass.getField("TYPE").get(null) == t.runtimeClass)
Some(x.asInstanceOf[A])
else super.unapply(x)
)
*/
override def unapply(x: Any): Option[A] = (
if (t.runtimeClass.isPrimitive) {
val ok = x match {
case _: java.lang.Integer => runtimeClass == java.lang.Integer.TYPE
//case _: java.lang.Double => runtimeClass == java.lang.Double.TYPE
case _: java.lang.Double => t == ClassTag.Double // equivalent
case _: java.lang.Long => runtimeClass == java.lang.Long.TYPE
case _: java.lang.Character => runtimeClass == java.lang.Character.TYPE
case _: java.lang.Float => runtimeClass == java.lang.Float.TYPE
case _: java.lang.Byte => runtimeClass == java.lang.Byte.TYPE
case _: java.lang.Short => runtimeClass == java.lang.Short.TYPE
case _: java.lang.Boolean => runtimeClass == java.lang.Boolean.TYPE
case _: Unit => runtimeClass == java.lang.Void.TYPE
case _ => false // super.unapply(x).isDefined
}
if (ok) Some(x.asInstanceOf[A]) else None
} else if (x == null) { // let them collect nulls, for example
if (t == ClassTag.Null) Some(null.asInstanceOf[A]) else None
} else super.unapply(x)
)
}
implicit def mytag[A](implicit t: ClassTag[A]): MyTag[A] = new MyTag(t)
// the one-liner
def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { case t(x) => x }
// this version loses the "null extraction", if that's a legitimate concept
//def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { case x: A => x }
g[Double](vs).print
g[Int](vs).print
g[Unit](vs).print
g[String](vs).print
g[Foo](vs).print
g[Null](vs).print
}
For 2.10.x, an extra line of boilerplate because implicit resolution is -- well, we won't say it's broken, we'll just say it doesn't work.
// simplified version for 2.10.x
object Test extends App {
implicit class Printable(val s: Any) extends AnyVal {
def print = Console println s.toString
}
case class Foo(s: String)
val vs = List(1,'a',(),"hi",2.3,4,Foo("big"),3.14,Foo("small"),(),null,'b',null)
import scala.reflect.{ ClassTag, classTag }
import scala.runtime.ScalaRunTime
// is a ClassTag for implicit use in case x: A
class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] {
override def runtimeClass = t.runtimeClass
override def unapply(x: Any): Option[A] = (
if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) &&
(x.getClass getField "TYPE" get null) == t.runtimeClass)
Some(x.asInstanceOf[A])
else t unapply x
)
}
// point of the exercise in implicits is the type pattern.
// there is no need to neutralize the incoming implicit by shadowing.
def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = {
implicit val u = new MyTag(t) // preferred as more specific
s collect { case x: A => x }
}
s"Doubles? ${g[Double](vs)}".print
s"Ints? ${g[Int](vs)}".print
s"Units? ${g[Unit](vs)}".print
s"Strings? ${g[String](vs)}".print
s"Foos? ${g[Foo](vs)}".print
}
Promoting a comment:
#WilfredSpringer Someone heard you. SI-6967