wilson tipos teoria sperber relevancia programacion inferencia ejemplos scala type-inference

scala - tipos - teoria de la relevancia ejemplos



La inferencia de tipo falla en Set made with.toSet? (3)

Estoy de acuerdo en que sería bueno inferir "el único posible" tipo, incluso cuando las llamadas están encadenadas, pero hay limitaciones técnicas.

Puede pensar en la inferencia como un barrido de amplitud sobre la expresión, recogiendo restricciones (que surgen de los límites de subtipo y los argumentos implícitos requeridos) en las variables de tipo, y luego resuelve esas restricciones. Este enfoque permite, por ejemplo, implicaciones para guiar la inferencia del tipo. En su ejemplo, aunque hay una única solución si solo mira la subexpresión xs.toSet , las llamadas encadenadas posteriores podrían introducir restricciones que hacen que el sistema no sea satisfactorio. La desventaja de dejar las variables de tipo sin resolver es que la inferencia de tipo para cierres requiere que se conozca el tipo de destino, y por lo tanto fallará (necesita algo concreto para seguir: el tipo requerido del cierre y el tipo de sus tipos de argumento deben no ambos son desconocidos).

Ahora, cuando el retraso en la resolución de las restricciones hace que la inferencia falle, podemos retroceder, resolver todas las variables de tipo y volver a intentar, pero esto es complicado de implementar (y probablemente bastante ineficiente).

¿Por qué falla la inferencia de tipos aquí?

scala> val xs = List(1, 2, 3, 3) xs: List[Int] = List(1, 2, 3, 3) scala> xs.toSet map(_*2) <console>:9: error: missing parameter type for expanded function ((x$1) => x$1.$times(2)) xs.toSet map(_*2)

Sin embargo, si se asigna xs.toSet , se compila.

scala> xs.toSet res42: scala.collection.immutable.Set[Int] = Set(1, 2, 3) scala> res42 map (_*2) res43: scala.collection.immutable.Set[Int] = Set(2, 4, 6)

Además, yendo para otro lado, convirtiendo a Set from List , y mapeando en List cumple.

scala> Set(5, 6, 7) res44: scala.collection.immutable.Set[Int] = Set(5, 6, 7) scala> res44.toList map(_*2) res45: List[Int] = List(10, 12, 14)


La inferencia de tipo no funciona correctamente ya que la firma de List#toSet es

def toSet[B >: A] => scala.collection.immutable.Set[B]

y el compilador necesitaría inferir los tipos en dos lugares en su llamada. Una alternativa para anotar el parámetro en su función sería invocar a toSet con un argumento de tipo explícito:

xs.toSet[Int] map (_*2)

ACTUALIZAR :

En cuanto a su pregunta de por qué el compilador puede inferirla en dos pasos, veamos lo que sucede cuando escribe las líneas una por una:

scala> val xs = List(1,2,3) xs: List[Int] = List(1, 2, 3) scala> val ys = xs.toSet ys: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

Aquí el compilador inferirá el tipo más específico para ys que es Set[Int] en este caso. Este tipo se conoce ahora, por lo que se puede inferir el tipo de función que se pasa al map .

Si completó todos los parámetros de tipo posibles en su ejemplo, la llamada se escribiría como:

xs.toSet[Int].map[Int,Set[Int]](_*2)

donde el segundo parámetro de tipo se utiliza para especificar el tipo de la colección devuelta (para más detalles, vea cómo se implementan las colecciones de Scala). Esto significa que incluso subestimé la cantidad de tipos que el compilador debe inferir.

En este caso, puede parecer fácil inferir Int pero hay casos en que no lo es (dadas las otras características de Scala como conversiones implícitas, tipos de singleton, rasgos como mixins, etc.). No digo que no se pueda hacer, es solo que el compilador de Scala no lo hace.


P: ¿Por qué no toSet lo que quiero?

A: Eso sería demasiado fácil.

P: ¿Pero por qué esto no compila? List(1).toSet.map(x => ...)

R: El compilador de Scala no puede inferir que x es un Int .

P: ¿Qué? ¿Es estúpido?

A: Bueno, List[A].toSet no devuelve un List[A].toSet immutable.Set[A] . List[A].toSet immutable.Set[A] . Devuelve un valor immutable.Set[B] para algún B >: A desconocido B >: A

P: ¿Cómo se supone que debo saber eso?

A: Del Scaladoc.

P: ¿Pero por qué se define así a toSet ?

R: Usted podría estar asumiendo que es immutable.Set es covariante, pero no lo es. Es invariante Y el tipo de toSet de toSet está en posición covariante, por lo que no se puede permitir que el tipo de devolución sea invariante.

P: ¿Qué quiere decir con "posición covariante"?

A: Déjame Wikipedia para ti: http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) . Ver también el capítulo 19 de Odersky, Venners & Spoon.

P: Lo entiendo ahora. Pero ¿por qué es inmutable. Establecer invariante?

A: Permítanme apilar Overflow para ustedes: ¿Por qué el conjunto inmutable de Scala no es covariante en su tipo?

P: Me rindo. ¿Cómo arreglo mi código original?

R: Esto funciona: List(1).toSet[Int].map(x => ...) . Lo mismo ocurre con: List(1).toSet.map((x: Int) => ...)

(con disculpas a Friedman & Felleisen. thx a paulp & ijuma por asistencia)

EDITAR: hay información adicional valiosa en la respuesta de Adriaan y en la discusión en los comentarios tanto aquí como aquí.