Хитрость вывода типов состоит в том, чтобы рассматривать его как процесс итеративного уточнения. Каждый блок параметров можно использовать для вывода некоторых параметров типа, которые затем можно использовать в последующих блоках. Итак, возьмите следующее определение:
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
является 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)
05.02.2011