2012-12-04 11 views
18

Se chiamo toSeq su una collezione immutable Set ottengo un ArrayBuffer.Perché toSeq di Scala converte un Set immutabile in un ArrayBuffer mutabile?

scala> Set(1,2,3).toSeq // returns Seq[Int] = ArrayBuffer(1, 2, 3) 

Questo mi sorprende. Data l'enfasi di Scala sull'uso di strutture di dati immutabili, mi aspetto di ottenere una sequenza immutabile come una Vector o List invece di una variabile ArrayBuffer. L'ordine restituito degli elementi dell'insieme dovrebbe ovviamente essere indefinito, ma non sembra esserci alcuna ragione semantica per cui anche questo ordine dovrebbe essere mutabile.

In generale, mi aspetto che le operazioni di Scala producano sempre risultati immutabili, a meno che non ne richieda esplicitamente una mutabile. Questa è stata la mia ipotesi da sempre, ma qui è errata, e in realtà ho appena trascorso un'ora a eseguire il debug di un problema in cui la presenza imprevista di un ArrayBuffer ha provocato un errore di runtime in un'istruzione match. La mia correzione era di cambiare Set(...).toSeq a Set(...).toList, ma questo sembra un trucco perché non c'è niente sulla mia applicazione che richiede un elenco in particolare a quel punto.

Avere Set(...).toSeq restituisce un oggetto mutabile un difetto nell'implementazione di Scala, o c'è un principio che sto fraintendendo qui?

Questo è Scala 2.9.2.

+0

fissato in 2.10-RC2: 'scala> Seq (1,2,3) .toSeq' ->' res0: Seq [Int] = List (1, 2, 3) ' – senia

+0

@senia, intendevi' Set (1,2,3) .toSeq' invece di 'Seq (1,2,3) .toSeq'? –

+1

Si prega di votare per il cambiamento: [SI-6570] (https://issues.scala-lang.org/browse/SI-6570) –

risposta

11

Here è il thread recente se Seq dovrebbe significare immutabile.Seq.

Roland Kuhn:

collection.Seq che non hanno mutators non è affatto una difesa valida!

L'esempio di varargs mutabile è piuttosto subdolo.

Recentemente,

scala> Set(1,2,3) 
res0: scala.collection.immutable.Set[Int] = Set(1, 2, 3) 

scala> res0.toSeq 
res1: Seq[Int] = ArrayBuffer(1, 2, 3) 

scala> res0.to[collection.immutable.Seq] 
res2: scala.collection.immutable.Seq[Int] = Vector(1, 2, 3) 
+0

Sembra che la risposta attuale sia "è controverso" e il thread collegato dà una buona discussione perché. Controlla anche i link nei commenti alla domanda originale, perché da loro sembra che gli anti-mutabilizzatori stiano facendo a modo loro. –

10

Sono d'accordo che è un po 'strano, ma non credo che sia un difetto. In primo luogo, considerare questo: il tipo in fase di compilazione della Set.toSeq è

() => Seq[Int] 

non

() => ArrayBuffer[Int] 

ArrayBuffer sembra appena essere il tipo di run-time dell'oggetto restituito (probabilmente perché Set.toSeq aggiunge a un ArrayBuffer e poi lo restituisce senza conversione).

Così, anche se toSeq sta dando indietro un oggetto mutabile, non si può in realtà mutarlo (senza fusione, o pattern matching per ArrayBuffer - così la parte reale "strana" è che Scala consente di corrispondenza modello su classi arbitrarie). (Devi confidare che lo Set non regga sull'oggetto e lo muti, ma penso che sia un presupposto equo da fare).

Un altro modo di guardarlo è che un tipo mutevole è strettamente più specifico di un tipo immutabile. Oppure, un altro modo per dire questo è che ogni oggetto mutevole può anche essere trattato come un oggetto immutabile: un oggetto immutabile ha un getter e un oggetto mutabile ha un getter e un setter - ma ha ancora un getter.

Naturalmente, questo può essere abusata:

val x = new Seq[Int] { 
    var n: Int = 0 
    def apply(k: Int) = k 
    def iterator = { 
     n += 1 
     (0 to n).iterator 
    } 
    def length = n 
} 

x foreach println _ 
0 
1 

x foreach println _ 
0 
1 
2 

ma, beh, così può un sacco di cose.