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)
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.
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