Domanda

I'm trying to write a macro that operates on a function parameter. I handled anonymous functions of all argument lengths pretty easily by making the parameter to the macro Any and then matching on Function(body, params), but I'd like to be able to pass in a reference to a function variable:

val valFunction = (something: Int) => (something * 3).toString
val functionVal = Macros.myMacro(valFunction)

I was able to use WeakTypeTags as per the official documentation to make this work with a function of one parameter:

def myMacro[T, U](param: T => U) = macro myMacroImpl[T, U]

def myMacroImpl[T, U](c: Context)(param: c.Expr[T => U])
     (implicit tt: c.WeakTypeTag[T], implicit ut: c.WeakTypeTag[T]) = {...}

but then the macro only works for functions of one parameter. I tried checking for a member named "apply" that was also a method, but I ran into erasure issues and also was concerned about overloaded methods and the such. The workaround I'm currently cringing my way through is to create 23 macros for Function0 through Function22, but whoo-boy I'm not happy about it. Is there another approach I can take?

È stato utile?

Soluzione 2

A possible approach to solve this problem is to create a simple type class that would be only available for functions:

trait Functionable[T]

With implicit macro that materializes implicit values:

object Functionable {
  implicit def materialize[T]: Functionable[T] = macro materialize_impl[T]
  def materialize_impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[Functionable[T]] = {
    val funcs = (0 to 22).map { c.universe.definitions.FunctionClass(_) }.toList
    if (funcs.exists { ttag.tpe.typeSymbol == _ })
      c.universe.reify { new Functionable[T] { } }
    else {
      c.abort(c.macroApplication.pos, "not a function")
    }
  }
}

This particular implementation won't work for subtypes but you can customize it if you need more.

And then we can define a macro as:

def myMacro[T](f: T)(implicit functionable: Functionable[T]) = macro myMacroImpl[T]
def myMacroImpl[T](c: Ctx)(f: c.Expr[T])(functionable: c.Expr[Functionable[T]])(implicit ttag: c.WeakTypeTag[T]): c.Expr[String] = c.literal(ttag.tpe.toString)

This macro will only be expanded for functions and will fail with compile-time implicit not found error if argument is not a function. From withing a macro you can reflectively get the info about types through weak type tag of the function type.

Whole example can be found here.

Altri suggerimenti

You'll probably have to accept Any (or unbound generic) as your macro parameter type and then manually check inside your macro that the argument is actually one of Function0 to Function22. This doesn't have to be that tedious, though:

val functionTypes = for(i <- 0 to 22) 
  yield c.mirror.staticClass(s"scala.Function$i").toType.erasure

Now, is my Tree's type one of the above?

functionTypes.exists(funTpe => tree.tpe <:< funTpe)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top