El truco para la inferencia de tipos es considerarla como un proceso de refinamiento iterativo. Cada bloque de parámetros se puede usar para inferir algunos de los parámetros de tipo, que luego se pueden usar en bloques posteriores. Así que toma la siguiente definición:
def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))
llamado como:
chain(2)(_*10)("xxx"+_)
Entonces, ¿cómo es esto inferido? Primero, comenzamos con el bloque (2)
que se sabe que tiene el tipo Int
. Sustituyendo de nuevo en que el parámetro T
obtenemos:
def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))
El siguiente bloque de parámetros es (_*10)
, donde ahora sabemos el tipo del marcador de posición _
ser Int
... y multiplicando una Int
por un Int
da otra Int
. Esto es cierto para el tipo devuelto incluso si ocurre un desbordamiento; en el extremo puede lanzar una excepción, pero las excepciones tienen el tipo Nothing
que es una subclase de todo lo demás en el sistema de tipo, por lo que todavía podemos decir Nothing
es-un Int
y el tipo inferido de Int
sigue siendo válido.
Con A
inferido, el método se convierte en:
def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))
única B
que se puede inferir de ("xxx"+_)
Dejando.Como String + Int
es una String
, el método es ahora:
def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))
Como el tipo de retorno del método viene directamente de fn2
, que también se pueden mostrar de forma explícita para la integridad:
def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))
Ahí lo tienes , todos los tipos se resuelven de forma segura, y el método demostrado ser estáticamente válido.
En su caso, es necesario el tipo T
debe inferirse antes de que sea posible inferir R
del tipo T=>R
. Para hacer esto, debe dividir los parámetros en dos bloques distintos, escribiendo el método en currículum:
def callOn[T,R](target: T)(f: (T => R)) = f(target)
Gracias por su respuesta. Supongamos que tengo este caso más simple: 'def callOn4 [R] (f: (Int => R)) = f (4)'. ¿Por qué puedo omitir el tipo de parámetro aquí, pero no si lo defino como 'def callOn4 (f: (Int => _)) = f (4)'? –
@jpp - solo intenta averiguar qué tipo de devolución se deduciría en tu segunda versión ... –