Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get constructor argument names using Scala-Macros

Is there a way to get parameter names of a given constructor using scala-macros?

Thanks

like image 432
mgonto Avatar asked Dec 11 '12 05:12

mgonto


2 Answers

Note that the :power approach in Paul Butcher's answer gives you access to internal APIs, which probably isn't either necessary or desired if you're trying to do this in a macro (or in runtime reflection outside of the REPL, for that matter).

So for example calling isConstructor on a plain old Symbol provided by members in the public Reflection API won't work—you first need to make sure that you have a MethodSymbol. Similarly with tpe. You could of course cast to the internal APIs in non-REPL code, but this is dangerous and unnecessary. The following is a better solution:

import scala.reflect.runtime.universe._

class Foo(name: String, i: Int) { def this(name: String) = this(name, 0) }

typeOf[Foo].declaration(nme.CONSTRUCTOR).asTerm.alternatives.collect {
  case m: MethodSymbol => m.paramss.map(_.map(_.name))
}

Or just:

typeOf[Foo].declarations.collect {
  case m: MethodSymbol if m.isConstructor => m.paramss.map(_.map(_.name))
}

Both of these will give you the following:

List(List(List(name, i)), List(List(name)))

As desired. I've used runtime reflection here to simplify the example, but this will work exactly the same way with the Universe you get from your Context in a macro.

like image 129
Travis Brown Avatar answered Nov 15 '22 04:11

Travis Brown


This REPL transcript should get you going, I hope:

Welcome to Scala version 2.10.0-RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_09).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

scala> class Foo(x: Int, y: Float)
defined class Foo

scala> (typeOf[Foo].members find (_.isConstructor)).get.tpe.params map (_.name)
res1: List[$r.intp.global.Symbol#NameType] = List(x, y)
like image 21
Paul Butcher Avatar answered Nov 15 '22 04:11

Paul Butcher