¿Existe alguna guía en Scala sobre cuándo usar val con una colección mutable versus usar var con una colección inmutable? ¿O realmente deberías apuntar a val con una colección inmutable?
El hecho de que existan ambos tipos de colección me da muchas opciones y, a menudo, no sé cómo tomar esa decisión.
scala
collections
functional-programming
immutability
James McCabe
fuente
fuente
Respuestas:
Esta es una pregunta bastante común. Lo difícil es encontrar los duplicados.
Debes luchar por la transparencia referencial . Lo que eso significa es que, si tengo una expresión "e", podría hacer una
val x = e
y reemplazarlae
porx
. Esta es la propiedad que rompe la mutabilidad. Siempre que necesite tomar una decisión de diseño, maximice la transparencia referencial.En la práctica, un método local
var
es el más segurovar
que existe, ya que no escapa al método. Si el método es corto, mejor aún. Si no es así, intente reducirlo extrayendo otros métodos.Por otro lado, una colección mutable tiene el potencial de escapar, incluso si no es así. Al cambiar el código, es posible que desee pasarlo a otros métodos o devolverlo. Ese es el tipo de cosas que rompe la transparencia referencial.
En un objeto (un campo) sucede más o menos lo mismo, pero con consecuencias más nefastas. De cualquier forma el objeto tendrá estado y, por tanto, romperá la transparencia referencial. Pero tener una colección mutable significa que incluso el objeto en sí mismo podría perder el control de quién lo está cambiando.
fuente
immutable val
duranteimmutable var
más demutable val
másmutable var
. ¡Especialmenteimmutable var
terminadomutable val
!var
. Otra característica interesante del uso de colecciones inmutables es que puedes mantener copias viejas de manera eficiente, incluso si lasvar
mutaciones.var x: Set[Int]
sobreval x: mutable.Set[Int]
ya que si pasax
a alguna otra función, en el primer caso, está seguro, esa función no puede mutarx
por usted.Si trabaja con colecciones inmutables y necesita "modificarlas", por ejemplo, agregarles elementos en un bucle, entonces debe usar
var
s porque necesita almacenar la colección resultante en algún lugar. Si solo lee de colecciones inmutables, useval
s.En general, asegúrese de no confundir referencias y objetos.
val
s son referencias inmutables (punteros constantes en C). Es decir, cuando lo useval x = new MutableFoo()
, podrá cambiar el objeto al quex
apunta, pero no podrá cambiar a qué objetox
apunta. Lo contrario es válido si usavar x = new ImmutableFoo()
. Siguiendo mi consejo inicial: si no necesita cambiar a qué objeto apunta un punto de referencia, useval
s.fuente
var immutable = something(); immutable = immutable.update(x)
frustra el propósito de usar una colección inmutable. Ya ha renunciado a la transparencia referencial y, por lo general, puede obtener el mismo efecto de una colección mutable con una mayor complejidad de tiempo. De las cuatro posibilidades (val
yvar
mutable e inmutable), esta tiene menos sentido. Yo uso a menudoval mutable
.var list: List[X] = Nil; list = item :: list; ...
y había olvidado que una vez escribí de manera diferente.La mejor forma de responder a esto es con un ejemplo. Supongamos que tenemos algún proceso que simplemente recopila números por alguna razón. Deseamos registrar estos números y enviaremos la colección a otro proceso para hacer esto.
Por supuesto, todavía estamos recopilando números después de enviar la colección al registrador. Y digamos que hay una sobrecarga en el proceso de registro que retrasa el registro real. Ojalá puedas ver a dónde va esto.
Si almacenamos esta colección en un mutable
val
, (mutable porque estamos agregando continuamente), esto significa que el proceso que realiza el registro buscará el mismo objeto que aún está siendo actualizado por nuestro proceso de recolección. Esa colección puede actualizarse en cualquier momento, por lo que cuando llegue el momento de iniciar sesión, es posible que no estemos registrando la colección que enviamos.Si usamos un inmutable
var
, enviamos una estructura de datos inmutable al registrador. Cuando agreguemos más números a nuestra colección, reemplazaremos nuestrovar
con una nueva estructura de datos inmutable . ¡Esto no significa que la colección enviada al registrador sea reemplazada! Todavía hace referencia a la colección que se envió. Entonces nuestro registrador registrará la colección que recibió.fuente
Creo que los ejemplos de esta publicación de blog arrojarán más luz, ya que la cuestión de qué combo usar se vuelve aún más importante en escenarios de concurrencia: la importancia de la inmutabilidad para la concurrencia . Y ya que estamos en eso, tenga en cuenta el uso preferido de sincronizado frente a @ volátil frente a algo como AtomicReference: tres herramientas
fuente
var immutable
vs.val mutable
Además de muchas excelentes respuestas a esta pregunta. A continuación, se muestra un ejemplo sencillo que ilustra los peligros potenciales de
val mutable
:Los objetos mutables se pueden modificar dentro de los métodos, que los toman como parámetros, mientras que no se permite la reasignación.
Resultado:
Algo como esto no puede suceder con
var immutable
, porque no se permite la reasignación:Resultados en:
Dado que los parámetros de función se tratan como
val
esta reasignación no está permitida.fuente
mutable val
no es posible conimmutable var
. ¿Qué es incorrecto aquí?+=
método como el búfer de matriz. Tus respuestas implican que+=
es lo mismox = x + y
que lo que no es. Su declaración de que los parámetros de función se tratan como vals es correcta y obtiene el error que menciona, pero solo porque usó=
. Puede obtener el mismo error con un ArrayBuffer, por lo que la mutabilidad de las colecciones aquí no es realmente relevante. Así que no es una buena respuesta porque no llega a lo que está hablando el OP. Aunque es un buen ejemplo de los peligros de pasar una colección mutable si no tenía la intención de hacerlo.ArrayBuffer
, usandoVector
. La pregunta del OP es amplia, pero estaban buscando sugerencias sobre cuándo usar cuál, así que creo que mi respuesta es útil porque ilustra los peligros de pasar una colección mutable (el hecho de queval
no ayude);immutable var
es más seguro quemutable val
.