2012-01-05 9 views
11

Considere case class Foo[A, B <: List[A]](l: B) { ... } o algo similar. En particular, A y B deben estar disponibles en algún lugar del cuerpo de Foo.Tipo básico inferral

¿Es posible que el compilador infiera A automáticamente? Por ejemplo, Foo(List(1,2,3)) falla ya que el verificador de tipos infiere A como Nothing. Tal vez hay una forma mediante el uso de miembros de tipo para resolver este problema?

que tienen que cierta sensación de que estoy pasando por alto algo embarazosamente simple aquí;)

EDIT: Me acabo de enterar que el uso de otro tipo de parámetro X funciona bien, pero atm No entiendo por qué es Entonces:

scala> case class Bar[A, B[X] <: List[X]](l: B[A]) 
defined class Bar 

scala> Bar(List(1,2,3)) 
res11: Bar[Int,List] = Bar(List(1, 2, 3)) 

¿Alguien me puede explicar esto? ¿Es esto un problema de unificación?

EDIT 2: Usar [A, B[X] <: List[X]](l: B[A]) puede tener implicaciones no deseadas para ciertas jerarquías (aunque no es realmente un gran problema). Más interesante, acabo de tropezar con un blog post de Josh Suereth que muestra implícitamente que [A, B <: List[A]](l: B with List[A]) funciona igual de bien ... No hay necesidad de implícitos, etc.

+0

¿Se puede definir la clase como 'case class Foo [A] (l: List [A])'? Eso podría ser más claro. – leedm777

+0

Entiendo lo que quiere decir, pero eso no es aplicable en mi caso. Tanto el tipo A como el B deben conocerse aparte el uno del otro. – fotNelton

Respuesta

2

De acuerdo con Alexey Romanov, en principio, el tipo podría inferirse automáticamente, pero simplemente no es en este momento.

Por lo tanto, al menos por ahora, todos los parámetros de tipo que deben inferirse tienen que aparecer explícitamente en los argumentos.

5

Realmente no responde la parte "por qué", lo siento, pero aquí hay algunos trucos más que puedes jugar. En primer lugar, ya que no se está utilizando el X en su ejemplo, puede escribir:

case class Bar[A,B[_] <: Seq[_]](l : B[A]) 

y luego:

scala> Bar(List(1,2,3)) 
resN: Bar[Int,List] = Bar(List(1, 2, 3)) 

(estoy usando el covariante Seq en lugar de List para mostrar funciona para subtipos también. Además, tenga en cuenta que esto no es equivalente a usar el X adicional, consulte los comentarios). Desafortunadamente, cada vez que desee utilizar el tipo de secuencia, debe escribir B[A], es decir, crear una instancia de forma manual. Una forma de evitar esto es escribir en su lugar:

case class Bar[A,B](l : B)(implicit ev : B <:< Seq[A]) 

en acción:

scala> Bar(List(1,2,3)) 
resN: Bar[Int,List[Int]] = Bar(List(1, 2, 3)) 

... y se obtiene los parámetros de tipo A y B instanciados al igual que siempre sabía lo que deberían.

+1

Este es un mal consejo. Nunca escriba "B [_] <: Seq [_]", no significa lo que cree que hace. Él de hecho está usando la X, para vincular el parámetro de tipo B a Seq. Los guiones bajos en "B [_] <: Seq [_]" no son del mismo tipo. – extempore

+0

Aparte del problema '_', encontré esto en una publicación interesante ya que nunca habría adivinado que una vista implícita podría ayudar. Entonces, ¿dónde encuentra el compilador una vista implícita para 'B' como' Seq [A] '? Cuando probé en la consola, encontré alguna función de Predef, pero también funciona con clases personalizadas (aparentemente no hay posibilidad de tener ejemplos muy formateados en esta sección, lo siento). ¿Hay alguna manera de rastrear qué implicaciones obtiene el compilador? – fotNelton

+0

Ah, vale, al mirar la fuente de Predef se revela la "infraestructura" de <: <, =: = y <% <. – fotNelton

5

No es posible para el compilador inferir A automáticamente. Pero si fuera posible, tendría que decir que A debería ser un supertipo de Int, tan Int o Any, no solo Int !. Porque una lista [Int] <: lista [cualquiera]. Entonces, si el compilador inferiría Int para A sería demasiado restrictivo.

En otras palabras:

  1. case class Foo[A, B <: List[A]](l: B) { ... } y luego calificó como Foo(List(1,2,3)) usted dice que A debe ser un tipo para el que sostiene que una lista de los que debería ser un supertipo de una lista de Int . Entonces, efectivamente, A debe ser un supertipo de Int porque Lista es covariante.

  2. case class Bar[A, B[X] <: List[X]](l: B[A]) y luego calificó como Foo(List(1,2,3)) que dicen que A debe ser un Int.

La caja (2) no deja espacio para que A sea algo más que Int. El caso (1) sin embargo deja espacio para que A sea algo más que Int, p. Ej. podría ser Cualquiera. Puede ver esto porque puede llamarlo al Foo[Any, List[Int]](List(1,2,3)). No podría hacer esto en el caso (2).

Por lo tanto, los dos casos no son equivalentes.

+0

Gracias, ese ejemplo de 'Foo [Any, List [Int]]' es bueno. OTOH Me pregunto por qué, siguiendo tu razonamiento, sigue siendo imposible decir algo como 'Foo [A, B>: List [A] <: List [A] ]' porque eso, desde un punto de vista lógico, eliminaría la ambigüedad (¿o no, me pregunto?). Sin embargo, en ambos casos, es decir, el último y el mencionado en mi publicación original, el compilador infiere 'A' como' Nothing' y sigue quejándose de eso (comprensible desde mi punto de vista limitado) y por eso estoy pensando que además del problema de la covarianza, ¿esto también se puede explicar en términos de unificación? – fotNelton

+0

Sí, de hecho, sigo tu razonamiento. Supongo que esto es lo que quiere decir con la inferencia de tipo de Scala incompleta. –

+0

Hay esta muy buena [conversación] (http://screencasts.chariotsolutions.com/webpage/2011/10) de Daniel Spiewack sobre (no solo de Scala) la inferencia de tipo incompleta. La introducción es bastante larga, pero toda la charla vale la pena. – fotNelton