How to get Scala function's parameters / retur

2019-04-27 23:05发布

I have a function, and would like to obtain its parameter types and return type for use in Scala macros.

scala> val fn = (a: String, b: Double) => 123
fn: (String, Double) => Int = <function2>

scala> fn.getClass
res1: Class[_ <: (String, Double) => Int] = class $anonfun$1

In the above example, the parameter types and return type already get printed at both lines, but I don't know how to access them. Even with toString I'd be stuck with the <function2> and class $anonfun$1 parts right of the = sign -- otherwise a bit of ugly string parsing might have done.

I found that the MethodSymbolApi offers a way to extract this information for methods, but it seems like this might not help for this particular case.

I'm currently looking into AST parsing (as part of scala.meta) to extract the information, but I'd think this question would seem basic enough to be covered by the standard reflection library, though I've failed to find what I want in there. Any ideas?

Edit based on @johanandren's answer:

I haven't found a neater way to extract them from the TypeTag/Type yet, but this does already work. :)

scala> val fn = (a: String, b: Double) => 123
scala> import scala.reflect.runtime.{universe => ru}
scala> def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
scala> getTypeTag(fn).tpe.toString.split(" => ")
res179: Array[String] = Array((String, Double), Int)

2条回答
男人必须洒脱
2楼-- · 2019-04-27 23:43

getClass is part of the Java reflection API which does not quite understand Scala types, you should look at the Scala Reflection API instead. This should get you started, http://docs.scala-lang.org/overviews/reflection/overview.html

Not sure but I think a TypeTag for the function type is what you want.

查看更多
三岁会撩人
3楼-- · 2019-04-27 23:48

Just for completness, when you are in Scala REPL, you can access the type as:

scala> val fn = (a: String, b: Double) => 123
fn: (String, Double) => Int = <function2>

scala> :type fn
(String, Double) => Int

At runtime, Scala compiler won't have complete type information. So it forms a code snippet fn, ( also does its own symbol table lookups ). https://github.com/scala/scala/blob/v2.10.5/src/compiler/scala/tools/nsc/interpreter/ILoop.scala#L449

Then passes it to the compiler which then attaches the type information to an implicit evidence type.

( explained here I want to get the type of a variable at runtime )

scala> import scala.reflect.runtime.universe.{TypeTag, typeTag}
import scala.reflect.runtime.universe.{TypeTag, typeTag}

scala> def getTypeTag[T: TypeTag](obj: T) = typeTag[T]
getTypeTag: [T](obj: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.TypeTag[T]

Now we have an evidence that will give us the exact type information:

scala> getTypeTag(fn)
res0: reflect.runtime.universe.TypeTag[(String, Double) => Int] = TypeTag[(String, Double) => Int]

scala> val targs = res0.tpe.typeArgs
targs: List[reflect.runtime.universe.Type] = List(String, Double, Int)

Now we can access the types with ease:

scala> val (in, out) = (ta.init, ta.last)
in: List[reflect.runtime.universe.Type] = List(String, Double)
out: reflect.runtime.universe.Type = Int

scala> println(s"INPUTS: $in")
INPUTS: List(String, Double)

scala> println(s"OUTPUT: $out")
OUTPUT: Int
查看更多
登录 后发表回答