2012-12-04 8 views
18

Jeśli zadzwonię pod numer toSeq w niezmiennej kolekcji Set, otrzymam numer ArrayBuffer.Dlaczego toSeq Scala zmienia niezmienny zestaw w zmienny ArrayBuffer?

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

To mnie zaskakuje. Biorąc pod uwagę nacisk Scala na użycie niezmiennych struktur danych, spodziewam się uzyskać niezmienną sekwencję, taką jak Vector lub List zamiast zmiennego ArrayBuffer. Zwrócona kolejność elementów zestawu powinna być oczywiście nieokreślona, ​​ale nie wydaje się, aby istniał jakiś semantyczny powód, dla którego ta kolejność również powinna być zmienna.

Generalnie spodziewam się, że operacje Scala zawsze będą generować niezmienne wyniki, chyba że wyraźnie zażądam zmiennego. To było moje założenie przez cały czas, ale jest niepoprawny tutaj i właściwie spędziłem godzinę debugując problem, w którym nieoczekiwana obecność ArrayBuffer doprowadziła do błędu runtime w instrukcji match. Moja poprawka miała zmienić się na Set(...).toSeq na Set(...).toList, ale to wygląda jak włamanie, ponieważ nie ma nic o mojej aplikacji, która w tym momencie wymaga listy.

Czy po otrzymaniu Set(...).toSeq zwrócono obiekt zmienny na lukę w implementacji Scali, czy też jest tu zasada, której tutaj nie rozumiem?

To jest Scala 2.9.2.

+0

stałej w 2,10 RC2: 'scala> Sek (1,2,3) .toSeq' -> 'res0 SEQ [Int] = Lista (1, 2, 3) ' – senia

+0

@senia, miałeś na myśli' Set (1,2,3) .toSeq' zamiast 'Seq (1,2,3) .toSeq'? –

+1

Głosuj za zmianą: [SI-6570] (https://issues.scala-lang.org/browse/SI-6570) –

Odpowiedz

11

Here to najnowszy wątek określający, czy Seq ma oznaczać niezmienną.Seq.

Roland Kuhn:

collection.Seq nie posiadające mutatory nie jest wcale ważnego obrony!

Przykład zmiennych varargs jest raczej podstępny.

Ostatnio

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

Wygląda na to, że obecna odpowiedź brzmi "jest kontrowersyjna", a połączony wątek daje dobre dyskusja dlaczego. Sprawdź także linki w komentarzach do pierwotnego pytania, ponieważ wydaje się, że anty-mutabiliści stają na ich drodze. –

10

Zgadzam się, że to trochę dziwne, ale nie sądzę, żeby to była wada. Po pierwsze, należy rozważyć to: typ kompilacji czas Set.toSeq jest

() => Seq[Int] 

nie

() => ArrayBuffer[Int] 

ArrayBuffer okazuje się być typu run-time zwróconego przedmiotu (prawdopodobnie dlatego Set.toSeq dodaje do ArrayBuffer a następnie po prostu zwraca to bez konwersji).

Więc chociaż toSeq daje Ci powrotem zmienny obiektu, nie można faktycznie mutować go (bez odlewania, lub wzorzec dopasowania do ArrayBuffer - tak prawdziwy „dziwne” jest to, że Scala pozwala Ci na dopasowanie wzoru dowolne klasy). (Musisz ufać, że Set nie trzyma się obiektu i nie mutuje go, ale myślę, że to uczciwe założenie do zrobienia).

Innym sposobem patrzenia na to jest, że zmienny typ jest po prostu bardziej specyficzny niż typ niezmienny. Innym sposobem na powiedzenie tego jest, że każdy obiekt podlegający mutacji może być również traktowany jako obiekt niezmienny: obiekt niezmienny ma element pobierający, a obiekt zmienny ma suwak ustawiający - ale nadal zawiera element pobierający.

Oczywiście, może to być nadużywane:

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 

ale dobrze, więc można wiele rzeczy.