2009-07-26 17 views
8

Supongamos que tengo dos clases, Input y Output, que están diseñadas para conectarse entre sí. Output produce valores de algún tipo, y Input los consume.¿Por qué Scala no puede inferir el parámetro de tipo en este ejemplo?

class Input[T] { 
    var output: Option[Output[_ <: T]] = None 
} 
class Output[T] { 
    var input: Option[Input[_ >: T]] = None 
} 

Está bien si un Input y Output par no operan en el mismo tipo de valor, siempre y cuando el parámetro de tipo Input es un supertipo del parámetro Output tipo. Tenga en cuenta que el parámetro de tipo en ambas clases es invariante; en las versiones reales se usa tanto en posiciones covariantes como contravariantes.

I tienen un método connect en otra parte que establece un enlace entre un Input/Output par:

def connect[T](output: Output[T], input: Input[_ >: T]) = { 
    output.input = Some(input) 
    input.output = Some(output) 
} 

Si llamo este método como a continuación, aparece un error de tipo:

val out = new Output[String] 
val in = new Input[AnyRef] 
connect(out, in) 

El error es:

test.scala:17: error: type mismatch; 
found : Output[String] 
required: Output[AnyRef] 
    connect(out, in) 
     ^

Puedo Resuelva esto escribiendo el parámetro tipo (en este caso, escribiría connect[String], pero creo que el compilador debería ser capaz de resolver esto por mí. ¿Cómo puedo cambiar el método connect para que el parámetro de tipo se deduzca automáticamente?


Editar: Por ahora, he hecho connect un método de Output por lo que recibe el parámetro de tipo de forma automática. Esto también tiene el beneficio adicional de que puedo usar la notación infija out connect in, pero el diseño se siente un poco incómodo.

Todavía estoy interesado en por qué el compilador muestra este comportamiento. Siento que debería ser capaz de inferir el parámetro de tipo. ¿Esto realmente funciona como se especifica?

+0

¿quisiste decir "no operar * en * el mismo tipo de valor" –

+0

¿Has intentado hacer la pregunta a la lista de correo de Scala? – GClaramunt

Respuesta

6

Va a veces obtener mejores resultados si utiliza múltiples listas de parámetros:

def connect[T](output: Output[T])(input: Input[_ >: T]) = { 
    output.input = Some(input) 
    input.output = Some(output) 
} 

connect(out)(in) 

... y, de hecho, en este caso, funciona.

+3

¿Puedes ampliar sobre por qué es esto? * "a veces se obtienen mejores resultados" * ¡no suena muy determinista! –

+6

Lamentablemente, el tipo inferencer no está especificado, y en ocasiones tampoco es determinista. –

0

Puedo estar totalmente equivocado, pero creo que el problema es cuando se une la entrada y la salida: la entrada tiene una salida restringida a un subtipo de T, pero la salida tiene una entrada restringida a un supertipo de T, el único tipo que puede satisfacer ambas condiciones es T.

Input[T] -> Output[ _ <: T ] 
Output[Q] -> Input[ _ >: Q ] 

Cuando se crea la entrada con la salida (en sustitución de Q con _ <: T) se obtiene:

Input[T]->Input[ _ >: [_ <: T] ] 

El mismo que

de entrada [E] -> Entrada [_ <: T <: _]

Por lo tanto el tipo de falta de coincidencia

+0

En mi ejemplo, String y AnyRef satisfacen mis limitaciones. La Salida produce Cadenas y requiere una Entrada que consume algún tipo de Supertipo de Cadena. La entrada consume AnyRefs y requiere una salida que produce algún subtipo de AnyRef. Dado que String es un subtipo de AnyRef, las restricciones se cumplen. –

+0

El problema es que hay exactamente un parámetro de tipo apropiado para conectar, y ese es el parámetro de tipo de la Salida. Cuando especifico esto explícitamente, funciona bien. Si no lo hago, intenta usar el parámetro de tipo de la entrada y obtengo un error de tipo en el argumento de salida. –

+0

Lo siento, leí mal la entrada [_>: T] en la firma de connect [T] ( – GClaramunt

0

En realidad, Scala Type Innerene no puede manejar la "recursión" por el momento. Entonces, si el tipo de argumento se puede inferir solo en la colocación con otro argumento, la falla no se puede deducir. Pero si utiliza la lista de argumentos diferentes, scala f(a)(b)(c,d) deducirá los tipos lista por lista, por lo que usualmente funciona mejor.

PD Es demasiado simplista, pero puede darle alguna pista.

Cuestiones relacionadas