Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Scala function's parameters / return type?

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)
like image 814
Kiara Grouwstra Avatar asked Sep 05 '15 15:09

Kiara Grouwstra


2 Answers

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.

like image 143
johanandren Avatar answered Sep 22 '22 10:09

johanandren


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
like image 45
tuxdna Avatar answered Sep 21 '22 10:09

tuxdna