Macros: accessing Function from a val defined function


#1

I am trying to collect and use a function’s name and argument/parameter names.
For functions defined with a def I can successfully match on these AST elements:

Function(params, _)
Block(_,Function(params,Apply(func,_)))

However when I define something like:

val h: Int => Double = (a:Int) => (2*a).toDouble

and try and analyze h, I can only match on:

Ident(term)

So my question is: is their a way for me to access the definition of the function h above? More concretely how can I know that h has the parameter a of type Int.

TIA


#2

let’s look at the tree shape:

% scala
Welcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_192).

scala 2.12.8> :power
Power mode enabled. :phase is at typer.
import scala.tools.nsc._, intp.global._, definitions._
Try :help or completions for vals._ and power._

scala 2.12.8> global.reify { val h: Int => Double = (a:Int) => (2*a).toDouble }
res0: $r.global.Expr[Unit] =
Expr[Unit]({
  val h: Function1[Int, Double] = ((a: Int) => 2.$times(a).toDouble);
  ()
})

scala 2.12.8> println(intp.global.nodePrinters.nodeToString(res0.tree))
Block(
  ValDef(
    0
    "h"
    AppliedTypeTree( // abstract trait Function1[-T1, +R] extends AnyRef in package scala
      "Function1" // abstract trait Function1[-T1, +R] extends AnyRef in package scala
      // 2 type arguments
      "Int" // final abstract class Int extends AnyVal in package scala
      "Double" // final abstract class Double extends AnyVal in package scala
    )
    Function(
      ValDef(
        <param>
        "a"
        "Int" // final abstract class Int extends AnyVal in package scala
        <empty>
      )
      2.$times(a)."toDouble"
    )
  )
  ()
)

It appears that the shape you’ll need to recognize is an outer ValDef (where you’ll find the val name) whose right-hand-side is a Function. Inside that Function is another ValDef that has the parameter names.


#3

Well, and I should probably also go on and say, if your macro is running in a context where all you have is a reference to h, then I think you’re out of luck. When you val h = ..., the right-hand-side can be literally any expression with the right type, and needn’t be a lambda, and the compiler won’t record any information about the specifics of that right-hand-side within h itself.

If you absolutely need to do this kind of global analysis (I say global because h could be defined anywhere), perhaps you need to be writing a compiler plugin rather than a macro. Although even that won’t necessarily help you unless you can count on h being defined in the same compilation unit, which you can’t count on in general.


#4

Appreciate the feedback. Indeed all I have is the val h. I am attempting to record information on functions that are passed via parameters. I guess I will have to simply fail when a val is used because they can be define in any compilation unit.

Thanks.