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:
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.
% 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.
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.
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.