Как получить параметры/тип возвращаемого значения функции Scala?

У меня есть функция, и я хотел бы получить ее типы параметров и возвращаемый тип для использования в макросах Scala.

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

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

В приведенном выше примере типы параметров и тип возвращаемого значения уже напечатаны в обеих строках, но я не знаю, как получить к ним доступ. Даже с toString я бы застрял с частями <function2> и class $anonfun$1 справа от знака =, иначе можно было бы немного уродливого синтаксического анализа строки.

Я обнаружил, что MethodSymbolApi предлагает способ извлечения этой информации для методов, но похоже, что это может не помочь в данном конкретном случае.

В настоящее время я изучаю синтаксический анализ AST (как часть scala.meta) для извлечения информации, но я думаю, что этот вопрос покажется достаточно простым, чтобы его можно было охватить стандартной библиотекой отражения, хотя мне не удалось найти то, что я хочу в там. Есть идеи?

Изменить на основе ответа @johanandren:

Я еще не нашел более аккуратного способа извлечь их из TypeTag/Type, но это уже работает. :)

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)

person Kiara Grouwstra    schedule 05.09.2015    source источник


Ответы (2)


getClass является частью API отражения Java, который не совсем понимает типы Scala, вместо этого вам следует взглянуть на API отражения Scala. Это должно помочь вам начать работу, http://docs.scala-lang.org/overviews/reflection/overview.html

Не уверен, но я думаю, что TypeTag для типа функции - это то, что вам нужно.

person johanandren    schedule 05.09.2015
comment
Спасибо; собирался вставить сюда рабочий вывод, но он не форматировался как код, поэтому добавил его к вопросу сейчас. - person Kiara Grouwstra; 05.09.2015
comment
Таким образом, A => R и т. д. на самом деле является синтаксическим сахаром для конкретных классов — Function1[A, R], Function2[A, B, R] и т. д., поэтому вы можете получить доступ к параметру типа, который является возвращаемым значением, взяв последний из них, например typeTag.tpe.typeArgs.last - person johanandren; 05.09.2015
comment
Примечание: параметры можно извлечь с помощью .init, а не .last. - person Kiara Grouwstra; 05.09.2015
comment
Извините, я думал, что вы были после возвращаемого значения только по какой-то странной причине :) - person johanandren; 05.09.2015

Просто для полноты, когда вы находитесь в Scala REPL, вы можете получить доступ к типу как:

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

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

Во время выполнения компилятор Scala не будет иметь полной информации о типе. Таким образом, он формирует фрагмент кода fn (также выполняет собственный поиск в таблице символов). https://github.com/scala/scala/blob/v2.10.5/src/compiler/scala/tools/nsc/interpreter/ILoop.scala#L449

Затем передает его компилятору, который прикрепляет информацию о типе к типу implicit evidence.

(объяснено здесь я хочу получить тип переменная во время выполнения )

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]

Теперь у нас есть свидетельство, которое даст нам точную информацию о типе:

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)

Теперь мы можем легко получить доступ к типам:

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
person tuxdna    schedule 05.09.2015