2012-01-06 7 views
6

la seguente dichiarazione compila bene e funziona come previsto:Non puoi aggiungere membri alla mappa utilizzando il tipo mixin dinamica per la chiave

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

Tuttavia, se provo ad aggiungere alla mappa:

map + ((3,4)) 

o

map + (("Bye", 4)) 

tanto sono un tipo non corrispondente:

Trovato: java.lang.String ("Ciao")

necessario: _ $ 1 quando tipo _ $ 1>: Int con stringa

Se indebolire la firma di tipo per consentire Any come digitare la chiave, quindi tutto funziona come previsto.

Il mio intuito dice che questo ha a che fare con la non varianza del tipo di chiave di Map e che $ 1 è in qualche modo risolto come un particolare tipo di Int with String, ma non sono particolarmente soddisfatto di questo. Qualcuno può spiegare cosa sta succedendo?

A cura di aggiungere:

Nel caso in cui vi state chiedendo dove questo si pone, è la firma che si ottiene se si fa qualcosa di simile:

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

risposta

9

Si fraintendono Int with String. Non è l'unione di Int e String, è l'intersezione, e per Int e String è vuota. Non gli insiemi di valori che sono Int con l'insieme di valori che sono stringhe, ma l'insieme di valori che hanno le caratteristiche di Int con le caratteristiche di String. Non ci sono valori simili.

È possibile utilizzare Either[Int, String] e disporre di Map[Left(1) -> 2, Right("Hello") -> 3). O non è esattamente l'Unione, è l'unione discriminata, A + B, piuttosto che A U B. Puoi cogliere la differenza in quanto O [Int, Int] non è la stessa cosa di Int. In realtà è isomorfo a (Int, Boolean): hai un Int, e sai da quale parte lo è anche. Quando A e B sono disgiunti (come Int e String) A + B e A U B sono isomorfi.

Oppure (non per i deboli di cuore) potete dare un'occhiata a a possible encoding of union types di Miles Sabin. (Non sono sicuro che potreste effettivamente usarlo con la mappa delle classi preesistente, e anche meno sicuro di quanto dovreste provare, ma ciò nonostante rende comunque molto interessante la lettura).


Edit: letto la tua domanda e il modo di codice troppo veloce, mi dispiace

Il tuo limite inferiore Int with String è lo stesso di Nothing, così Map[_ >: Int with String, Int], è lo stesso che Map[_ >: Nothing, Int] e come il Nothing limite inferiore è implicito, questo è Map[_, Int]. Il tuo attuale Map è un Map[Any, Int]. Potresti aver aggiunto una chiave booleana, funziona anche, nonostante Int con String. A Map[Any, Int] può essere digitato come Map[_, Int] così la vostra dichiarazione val funziona. Ma la tua digitazione perde tutte le informazioni sul tipo di chiave.Non sapendo quale sia il tipo di chiave, non è possibile aggiungere (né recuperare) nulla dalla tabella.

Un UpperBound non sarebbe stato migliore, in quanto quindi non esiste una chiave possibile. Anche la dichiarazione iniziale non ha esito positivo.


Edit 2: per quanto riguarda if (true) Map(1 -> 2) else Map("1" -> 2)

Questa non è la stessa cosa di Map(1 -> 2, "1" -> 2). È stato più semplice, semplicemente un Map[Any, Int], poiché Any è il più grande supertipo comune di Int e String.

D'altra parte, Map(1 -> 2) è un Map[Int, Int] e Map["1", 2] a Map[String, Int]. Vi è il problema di trovare un supertipo comune per Map[Int, Int] e Map[String, Int], che non sta trovando un supertipo comune di Int e String.

Facciamo esperimenti. Map è covariante nel suo secondo parametro. Se si utilizza Int e String come valori, piuttosto che le chiavi:

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

Con covarianza, si prende semplicemente il supertipo comune di tutti i parametri di tipo.

Con un tipo controvariante:

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] 

si prende l'intersezione A with C. Quando B è un sottotipo di A, A con B è solo B.

Ora con il parametro non variante Map quando i due tipi sono legati

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

Tale tipo non è inutile. È possibile accedere o aggiungere valori con le chiavi di tipo B. Ma non è possibile accedere ai valori delle chiavi A. Dato che questo è quello che hai nella mappa attuale (a causa dello true), sfortuna. Se si accede allo keySet, verrà digitato Set[A]. Hai informazioni incomplete sul tipo di chiavi e ciò che puoi fare è limitato, ma questa è una limitazione necessaria, data la conoscenza limitata che hai sul tipo di mappa. Con Int and String, si dispone di informazioni minime, con un limite inferiore Any e un limite superiore equivalente a Nothing. Il limite superiore di Nothing rende impossibile chiamare una routine che accetta una chiave come parametro. È ancora possibile recuperare keySet, con il tipo Set[Any], Any come limite inferiore.

+0

Non penso che questo sia il problema - 'Int with String' è legato al tipo di chiave, non al tipo stesso. Posso usarlo come un limite in molte altre situazioni. – Submonoid

+0

La codifica dei tipi di unione è eccezionale, a proposito, ma non particolarmente pertinente alla domanda - in particolare, mi interessa sapere perché il compito è valido ma provare ad aggiungere qualcos'altro fallisce. – Submonoid

+0

Penso che sia un limite completamente inutile, vedi complemento per rispondere. –

Problemi correlati