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