Domanda Perché gli array sono invarianti, ma le liste sono covarianti?


Per esempio. perché lo fa

val list:List[Any] = List[Int](1,2,3)

lavoro, ma

val arr:Array[Any] = Array[Int](1,2,3)

fallisce (perché gli array sono invarianti). Qual è l'effetto desiderato dietro questa decisione di progettazione?


44
2017-07-13 19:30


origine


risposte:


Perché altrimenti si spezzerebbe la sicurezza del tipo. Altrimenti, saresti in grado di fare qualcosa del genere:

val arr:Array[Int] = Array[Int](1,2,3)
val arr2:Array[Any] = arr
arr2(0) = 2.54

e il compilatore non può prenderlo.

D'altra parte, le liste sono immutabili, quindi non è possibile aggiungere qualcosa che non lo è Int


66
2017-07-13 19:34



Questo perché le liste sono immutabili e gli array sono mutabili.


27
2017-07-13 19:33



La differenza è questa Lists sono immutabili mentre Arrays sono mutabili.

Per capire perché la mutabilità determina la varianza, prendere in considerazione la possibilità di creare una versione mutabile di List - chiamiamolo MutableList. Faremo anche uso di alcuni tipi di esempio: una classe base Animal e 2 sottoclassi nominate Cat e Dog.

trait Animal {
  def makeSound: String
}

class Cat extends Animal {
  def makeSound = "meow"
  def jump = // ...
}

class Dog extends Animal {
  def makeSound = "bark"
}

Notare che Cat ha un altro metodo (jump) di Dog.

Quindi, definire una funzione che accetta un elenco mutabile di animali e modifica l'elenco:

def mindlessFunc(xs: MutableList[Animal]) = {
  xs += new Dog()
}

Ora, accadranno cose orribili se passerai una lista di gatti nella funzione:

val cats = MutableList[Cat](cat1, cat2)
val horror = mindlessFunc(cats)

Se usassimo un linguaggio di programmazione trascurato, questo verrà ignorato durante la compilazione. Tuttavia, il nostro mondo non collasserà se accediamo solo all'elenco dei gatti usando il seguente codice:

cats.foreach(c => c.makeSound)

Ma se lo facciamo:

cats.foreach(c => c.jump)

Si verificherà un errore di runtime. Con Scala, la scrittura di questo codice è impedita, perché il compilatore si lamenterà.


4
2017-07-01 16:06



La risposta normale da dare è che la mutabilità combinata con la covarianza romperebbe la sicurezza del tipo. Per le collezioni, questo può essere considerato come una verità fondamentale. Ma la teoria si applica in realtà a qualsiasi tipo generico, non solo a collezioni come List e Arraye non dobbiamo assolutamente tentare di ragionare sulla mutabilità.

La vera risposta ha a che fare con il modo in cui i tipi di funzione interagiscono con i sottotitoli. La storia breve è se un parametro di tipo è usato come tipo di ritorno, è covariante. D'altra parte, se un parametro di tipo viene utilizzato come tipo di argomento, è controverso. Se è usato sia come tipo di ritorno che come tipo di argomento, è invariante.

Diamo un'occhiata a documentazione per Array[T]. I due metodi ovvi da considerare sono quelli per la ricerca e l'aggiornamento:

def apply(i: Int): T
def update(i: Int, x: T): Unit

Nel primo metodo T è un tipo di ritorno, mentre nel secondo T è un tipo di argomento. Le regole di varianza dettano questo T deve quindi essere invariante.

Possiamo confrontare il documentazione per List[A] per vedere perché è covariante. Confusamente, troveremmo questi metodi, che sono analoghi ai metodi per Array[T]:

def apply(n: Int): A
def ::(x: A): List[A]

Da A è usato sia come tipo di ritorno che come tipo di argomento, ci aspetteremmo A essere invariante come T è per Array[T]. Tuttavia, a differenza di Array[T], la documentazione ci sta mentendo sul tipo di ::. La bugia è abbastanza buona per la maggior parte delle chiamate a questo metodo, ma non è abbastanza buona per decidere la varianza di A. Se espandiamo la documentazione per questo metodo e clicchiamo su "Full Signature", ci viene mostrata la verità:

def ::[B >: A](x: B): List[B]

Così A in realtà non appare come un tipo di argomento. Anziché, B (che può essere qualsiasi supertipo di A) è il tipo di argomento. Questo non pone alcuna restrizione su A, quindi può davvero essere covariante. Qualsiasi metodo su List[A] che ha A come tipo di argomento è una bugia simile (possiamo dire perché questi metodi sono contrassegnati come [use case]).


4
2018-03-10 14:15