How to write macro annotation which looks in usage like @named("+2") _ + 2
and produces:
new (Int => Int) {
override def toString(): String = "+2"
def apply(x: Int): Int = x + 2
}
How to write macro annotation which looks in usage like @named("+2") _ + 2
and produces:
new (Int => Int) {
override def toString(): String = "+2"
def apply(x: Int): Int = x + 2
}
You can create macro that returns an anonymous function. You are not getting completely the syntax you want, seems like @ does not work inside methods.
import scala.language.experimental.macros
import scala.reflect.macros._
object Named {
def build[T, R](name: String)(applyFunc: T => R): T => R = macro Named.impl[T, R]
def impl[T: c.WeakTypeTag, R: c.WeakTypeTag](c: whitebox.Context)(name: c.Expr[String])(applyFunc: c.Expr[T => R]): c.Expr[T => R] = {
import c.universe._
val functionType = weakTypeOf[T]
val resultType = weakTypeOf[R]
c.Expr[T => R](
c.typecheck(q"""
new ($functionType => $resultType) {
override def toString() = $name
def apply(x: $functionType): $resultType = $applyFunc(x)
}
"""))
}
}
and then use this macro to generate your own function:
class NamedTest {
@Test
def testNamed() = {
val b = Named.build[Int, Int]("+2")(_ + 2)
assertEquals(4, b(2))
assertEquals("+2", b.toString)
}
}
Correct syntax is
((_: Int) + 2): @named("+2")
. Unfortunately macro annotations annotating expressions don't expand.The simplest is to use
without any macros.
Otherwise Scalameta can expand annotations on expressions:
build.sbt (sbt documentation about generation of sources is here)
project/build.sbt
project/Generator.scala
project/Transformer.scala
annotations/src/main/scala/com/example/annotations/named.scala
helpers/src/main/scala/com/example/helpers/TypedFunctions.scala
in/src/main/scala/com/example/App.scala
out/target/scala-2.13/src_managed/main/scala/com/example/App.scala (after
sbt "; project out; clean; compile"
)Another example is How to merge multiple imports in scala?