NietzscheanAI
NietzscheanAI

Reputation: 966

Passing and invoking a higher-order function in a macro

Following on from Compilation issue when accessing parameter value in Scala macro, I want to define a macro that applies a predicate. If the statement fn = c.eval( pred ) is present, then client code fails to compile, without any indication as to the nature of the problem.

def fnInvocation( value : Int, pred : c.Expr[ Int => Boolean ] ): Boolean = fnInvocationImpl

def fnInvocationImpl(c: Context)( value : Int, pred : c.Expr[ Int => Boolean ] ) : c.Expr[Boolean] = {
    var fn = x => x % 2 == 0
    //    fn = c.eval( pred ) // client compilation fails if this line is included
    val result = fn( value )
    c.literal( result )
}

Should I be able to do this from within a macro?

Upvotes: 2

Views: 455

Answers (1)

Travis Brown
Travis Brown

Reputation: 139028

Macro methods themselves don't have an initial context parameter list, and it doesn't make sense for them to have expression arguments. In their implementations, on the other hand, every argument (after the initial parameter list with the context) must be an expression.

In your case the signatures would need to look like this:

def fnInvocation(value: Int, pred: Int => Boolean): Boolean =
  macro fnInvocationImpl

def fnInvocationImpl(c: Context)(
  value: c.Expr[Int],
  pred: c.Expr[Int => Boolean]
): c.Expr[Boolean] = ???

The comments in my previous answer apply here as well. If both arguments are compile-time literals, you can perform the application at compile-time—see my gist here for an example of how this kind of thing works with function literals. If they're not both literals, you can build a tree that performs the application at runtime. You could even check whether the arguments are both literals, apply the function and return a compile-time literal if they are, and fall back to generating a tree otherwise. What you can't do is perform the application at compile-time if the values aren't literals.

Upvotes: 1

Related Questions