Когда Scala нужны типы параметров для анонимных и расширенных функций? - PullRequest
7 голосов
/ 04 февраля 2011

Когда компилятору Scala действительно нужна информация о типе параметров анонимных функций?

Например, учитывая эту функцию:

def callOn[T,R](target: T, f: (T => R)) = f(target)

, тогда я не могу использовать ее следующим образом:

callOn(4, _.toString)
  => error: missing parameter type for expanded function ((x$1) => x$1.toString)

и я должен указать

callOn(4, (_: Int).toString)

, что довольно некрасиво.Почему мой пример не работает, в то время как методы типа map, filter, foldLeft и т. Д. В классах коллекций не нуждаются в этом явном типе?

Ответы [ 2 ]

14 голосов
/ 05 февраля 2011

Хитрость для вывода типа заключается в том, чтобы рассматривать его как процесс итеративного уточнения.Каждый блок параметров можно использовать для вывода некоторых параметров типа, которые затем можно использовать в последующих блоках.Итак, примите следующее определение:

def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))

, называемое:

chain(2)(_*10)("xxx"+_)

Так как же это сделать?Сначала мы начнем с блока (2), который, как известно, имеет тип Int.Подставляя это обратно в параметр T, мы получаем:

def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))

Следующий блок параметров - (_*10), где мы теперь знаем тип заполнителя _, равный Int ...умножение Int на Int дает еще один Int.Это верно для возвращаемого типа, даже если происходит переполнение;в крайнем случае он может выдать исключение, но исключения имеют тип Nothing, который является подклассом всего остального в системе типов, поэтому мы все еще можем сказать Nothing is-an Int и предполагаемый тип Int все еще действителен.

С выводом A метод становится следующим:

def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))

Оставляя только B, который может быть выведен из ("xxx"+_).Поскольку String + Int является String, метод теперь выглядит следующим образом:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))

Поскольку тип возвращаемого значения метода приходит непосредственно из fn2, это также может быть явно показано для полноты:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))

Там у вас это есть, все типы безопасно разрешены, и метод признан статически действительным.


В вашем случае вам нужен тип T, чтобы сделать выводможно вывести R из типа T=>R.Для этого необходимо разбить параметры на два отдельных блока, записав метод в форме карри:

def callOn[T,R](target: T)(f: (T => R)) = f(target)
6 голосов
/ 04 февраля 2011

Здесь также был дан ответ на этот вопрос:

Передача функций для всех применимых типов вокруг

Вы ожидаете, ... что компилятор Scala приметучесть оба параметра, чтобы дважды вывести правильные типы.Однако Scala этого не делает - она ​​использует информацию только из одного списка параметров в следующий, но не из одного параметра в другой.Это означает, что параметры f и a [WS: target и f в этом случае] анализируются независимо, не имея преимущества в знании того, что является другим.

Обратите внимание, что это делает работа с карри версией:

scala> def callOn[T,R](target: T)(f: (T => R)) = f(target)
callOn: [T,R](target: T)(f: (T) => R)R

scala> callOn(4)(_.toString)
res0: java.lang.String = 4

scala> 
...