Andriy Plokhotnyuk Andriy Plokhotnyuk - 18 days ago 5
Scala Question

Macro annotation to override toString of Scala function

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
}

Answer

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)
  }
}