2012-01-06 11 views
6

la siguiente declaración compila bien y funciona como se esperaba:No se pueden agregar miembros a Mapa utilizando el tipo de mixin dinámica de la clave

val map : Map[_ >: Int with String, Int] = Map(1 -> 2, "Hello" -> 3) 

Sin embargo, si intento agregar al mapa:

map + ((3,4)) 

o

map + (("Bye", 4)) 

cuando me siento un tipo de desajuste:

encontrado: java.lang.String ("adiós")

requiere: _ $ 1 en la que el tipo _ $ 1>: Int con la cadena

Si debilitar la firma de tipo para permitir que el Any tipo de clave, entonces esto funciona como se esperaba.

Mi intuición dice que esto tiene que ver con la no varianza del tipo de clave de Map, y que _ $ 1 se está reparando de alguna manera como un supertipo particular de Int with String, pero no estoy particularmente contento con esto. ¿Alguien puede explicar lo que está pasando?

Editado para añadir:

En caso de que usted se pregunta dónde surge esta, que es la firma que se obtiene si se hace algo como:

val map = if (true) Map(1 -> 2) else Map("1" -> 2) 

Respuesta

9

usted no entiende Int with String. No es la Unión de Int y String, es la intersección, y para Int y String está vacía. No son los conjuntos de valores que son Int con el conjunto de valores que son cadenas, sino el conjunto de valores que tienen las características de Int con las características de Cadena también. No hay tales valores.

Puede usar Either[Int, String] y tener Map[Left(1) -> 2, Right("Hello") -> 3). O bien no es exactamente la Unión, es la unión discriminada, A + B, en lugar de A U B. Puede captar la diferencia en que O bien [Int, Int] no es lo mismo que Int. De hecho, es isomorfo a (Int, Boolean): tienes un Int, y sabes de qué lado está. Cuando A y B son disjuntos (como son Int y String) A + B y A U B son isomorfos.

O (no apto para cardíacos) puede echar un vistazo a a possible encoding of union types de Miles Sabin. (No estoy seguro de que puedas usar eso con un mapa de clase preexistente, y aún menos seguro de lo que deberías intentar, pero de todos modos es una lectura muy interesante).


Editar: Lea la pregunta y forma de código demasiado rápido, lo siento

Su límite inferior Int with String es lo mismo que Nothing, por lo Map[_ >: Int with String, Int], es el mismo que Map[_ >: Nothing, Int] y como el Nothing límite inferior es implicado, esto es Map[_, Int]. Su actual Map es un Map[Any, Int]. Podría haber agregado una clave booleana, también funciona, a pesar del Int con String. Un Map[Any, Int] se puede escribir como Map[_, Int] para que su declaración de validación funcione. Pero su mecanografía pierde toda la información sobre el tipo de clave.Sin saber cuál es el tipo de clave, no puede agregar (ni recuperar) nada de la tabla.

Un UpperBound no hubiera sido mejor, ya que entonces no hay una clave posible. Incluso la declaración val inicial falla.


Editar 2: En cuanto a if (true) Map(1 -> 2) else Map("1" -> 2)

Esto no es lo mismo que Map(1 -> 2, "1" -> 2). Eso era más simple, simplemente un Map[Any, Int], como Any es el mayor supertipo común de Int y String.

Por otro lado, Map(1 -> 2) es Map[Int, Int], y Map["1", 2] a Map[String, Int]. Existe el problema de encontrar un supertipo común para Map[Int, Int] y Map[String, Int], que no encuentra un supertipo común de Int y String.

Vamos a experimentar. Map es covariante en su segundo parámetro. Si utiliza Int y String como valores en lugar de teclas:

if (true) Map(1 -> 2) else Map(1 -> "2") 
res1: scala.collection.immutable.Map[Int, Any] 

Con covarianza, simplemente toma el supertipo común de todos los parámetros de tipo.

Con un tipo contravariant:

class L[-T] 
object L{def apply[T](t: T) = new L[T]) 
class A 
class B extends A 
class C 
if (true) L(new A) else L(new C) 
res2: L[A with C] 
if (true) L(new A) else L(new B) 
res3: L[B] 

que toma la intersección A with C. Cuando B es un subtipo de A, A con B es sólo B.

Ahora, con el parámetro de no variante de Mapa cuando los dos tipos están relacionados

if (true) Map(new A -> 1) else Map(new B -> 1) 
res4: scala.collection.immutable.Map[_ >: B <: A, Int] 

Tal tipo no es inútil. Puede acceder o agregar valores con las claves del tipo B. Pero no puede acceder a los valores de las claves A. Como esto es lo que tienes en el mapa real (debido al true), mala suerte. Si accede al keySet, se tecleará Set[A]. Tiene información incompleta sobre el tipo de claves, y lo que puede hacer es limitado, pero esta es una limitación necesaria, dado el conocimiento limitado que tiene sobre el tipo de mapa. Con Int and String, tiene información mínima, con un límite inferior Any y un límite superior equivalente a Nothing. El límite superior Nothing no permite llamar a una rutina que toma una tecla como parámetro. Todavía puede recuperar el keySet, con el tipo Set[Any], Any siendo el límite inferior.

+0

No creo que este sea el problema - 'Int with String' está sujeto al tipo de clave, no al tipo en sí. Puedo usarlo como un límite en muchas otras situaciones. – Submonoid

+0

Por cierto, la codificación de los tipos de unión es increíble, pero no es especialmente relevante para la pregunta; en particular, me interesa saber por qué la asignación es válida pero si intenta agregar algo más, falla. – Submonoid

+0

Creo que es un límite completamente inútil, ver complemento para responder. –

Cuestiones relacionadas