val array scala set sequence immutability scala-collections

val - ¿Por qué el toSeq de Scala convierte un conjunto inmutable en un ArrayBuffer mutable?



scala list (2)

Si llamo toSeq en una colección de Set inmutable, obtengo un ArrayBuffer .

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

Esto me sorprende. Dado el énfasis de Scala en el uso de estructuras de datos inmutables, espero recuperar una secuencia inmutable como un Vector o List lugar de un ArrayBuffer mutable. Por supuesto, el orden devuelto de los elementos del conjunto debe ser indefinido, pero no parece haber ninguna razón semántica por la que ese orden también deba ser mutable.

En general, espero que las operaciones de Scala siempre produzcan resultados inmutables, a menos que solicite explícitamente uno mutable. Este ha sido mi supuesto desde el principio, pero es incorrecto aquí, y en realidad acabo de pasar una hora depurando un problema donde la inesperada presencia de un ArrayBuffer provocó un error de ejecución en una declaración de match . Mi solución fue cambiar Set(...).toSeq para Set(...).toList , pero se siente como un hack porque no hay nada en mi aplicación que requiera una lista en particular en ese momento.

¿Tener Set(...).toSeq devolver un objeto mutable es un defecto en la implementación de Scala, o hay un principio que estoy entendiendo mal aquí?

Esto es Scala 2.9.2.


Estoy de acuerdo en que es un poco extraño, pero no creo que sea una falla. Primero, considera esto: el tipo de tiempo de Set.toSeq de Set.toSeq es

() => Seq[Int]

no

() => ArrayBuffer[Int]

ArrayBuffer es el tipo de tiempo de ejecución del objeto devuelto (probablemente porque Set.toSeq agrega a un ArrayBuffer y luego simplemente lo devuelve sin convertir).

Por lo tanto, aunque toSeq le devuelva un objeto mutable, en realidad no puede mutarlo (sin lanzar, o hacer coincidir el patrón con ArrayBuffer , por lo que la parte "extraña" es que Scala le permite establecer el patrón en clases arbitrarias). (Debes confiar en que Set no se aferra al objeto y lo muta, pero creo que es una suposición razonable).

Otra forma de verlo es que un tipo mutable es estrictamente más específico que un tipo inmutable. O, otra forma de decir esto es que cada objeto mutable también puede tratarse como un objeto inmutable: un objeto inmutable tiene un captador, y un objeto mutable tiene un captador y un establecedor, pero todavía tiene un captador.

Por supuesto, esto puede ser abusado:

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

Pero, bueno, también muchas cosas.


Here está el hilo reciente sobre si Seq debería significar immutable.Seq.

Roland Kuhn:

collection.Seq no tener mutadores no es en absoluto una defensa válida!

El ejemplo de varargs mutables es bastante astuto.

Recientemente,

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)