Existe-t-il des directives dans Scala sur le moment d'utiliser val avec une collection mutable par rapport à l'utilisation de var avec une collection immuable? Ou devriez-vous vraiment viser le val avec une collection immuable?
Le fait qu'il existe les deux types de collections me donne beaucoup de choix, et souvent je ne sais pas comment faire ce choix.
scala
collections
functional-programming
immutability
James McCabe
la source
la source
Réponses:
Question assez courante, celle-ci. Le plus difficile est de trouver les doublons.
Vous devez viser la transparence référentielle . Cela signifie que si j'ai une expression «e», je pourrais créer un
val x = e
et le remplacere
parx
. C'est la propriété qui rompt la mutabilité. Chaque fois que vous avez besoin de prendre une décision de conception, maximisez la transparence référentielle.En pratique, une méthode locale
var
est la plus sûrevar
qui existe, car elle n'échappe pas à la méthode. Si la méthode est courte, c'est encore mieux. Si ce n'est pas le cas, essayez de le réduire en extrayant d'autres méthodes.D'un autre côté, une collection mutable a le potentiel de s'échapper, même si ce n'est pas le cas. Lors du changement de code, vous pouvez alors vouloir le passer à d'autres méthodes ou le renvoyer. C'est le genre de chose qui brise la transparence référentielle.
Sur un objet (un champ), à peu près la même chose se produit, mais avec des conséquences plus désastreuses. Dans tous les cas, l'objet aura un état et, par conséquent, rompra la transparence référentielle. Mais avoir une collection mutable signifie que même l'objet lui-même peut perdre le contrôle de qui le change.
la source
immutable val
plusimmutable var
surmutable val
plusmutable var
. Surtoutimmutable var
finimutable val
!var
. Une autre fonctionnalité intéressante de l'utilisation de collections immuables est que vous pouvez conserver efficacement les anciennes copies, même si ellesvar
mute.var x: Set[Int]
plusval x: mutable.Set[Int]
car si vous passezx
à une autre fonction, dans le premier cas, vous êtes sûr que cette fonction ne peut pas muterx
pour vous.Si vous travaillez avec des collections immuables et que vous avez besoin de les «modifier», par exemple, y ajouter des éléments dans une boucle, vous devez utiliser
var
s car vous devez stocker la collection résultante quelque part. Si vous ne lisez qu'à partir de collections immuables, utilisezval
s.En général, assurez-vous de ne pas confondre références et objets.
val
s sont des références immuables (pointeurs constants en C). Autrement dit, lorsque vous utilisezval x = new MutableFoo()
, vous pourrez changer l'objet vers lequelx
pointe, mais vous ne pourrez pas changer vers quel objetx
pointe. Le contraire est vrai si vous utilisezvar x = new ImmutableFoo()
. Reprenant mon premier conseil: si vous n'avez pas besoin de changer vers quel objet une référence pointe, utilisezval
s.la source
var immutable = something(); immutable = immutable.update(x)
va à l'encontre de l'objectif d'utiliser une collection immuable. Vous avez déjà abandonné la transparence référentielle et vous pouvez généralement obtenir le même effet à partir d'une collection mutable avec une meilleure complexité temporelle. Des quatre possibilités (val
etvar
, mutables et immuables), celle-ci a le moins de sens. J'utilise souventval mutable
.var list: List[X] = Nil; list = item :: list; ...
et j'avais oublié que j'avais écrit différemment une fois.La meilleure façon de répondre est de donner un exemple. Supposons que nous ayons un processus qui collecte simplement des nombres pour une raison quelconque. Nous souhaitons enregistrer ces numéros et enverrons la collection à un autre processus pour ce faire.
Bien sûr, nous continuons à collecter des numéros après avoir envoyé la collection à l'enregistreur. Et disons qu'il y a une surcharge dans le processus de journalisation qui retarde la journalisation réelle. J'espère que vous pouvez voir où cela va.
Si nous stockons cette collection dans un mutable
val
, (mutable car nous y ajoutons continuellement), cela signifie que le processus effectuant la journalisation examinera le même objet qui est toujours mis à jour par notre processus de collecte. Cette collection peut être mise à jour à tout moment, et donc quand il est temps de se connecter, il se peut que nous n'enregistrions pas réellement la collection que nous avons envoyée.Si nous utilisons un immuable
var
, nous envoyons une structure de données immuable à l'enregistreur. Lorsque nous ajouterons plus de numéros à notre collection, nous remplacerons notrevar
par une nouvelle structure de données immuable . Cela ne signifie pas que la collecte envoyée à l'enregistreur est remplacée! Il fait toujours référence à la collection qui lui a été envoyée. Notre enregistreur enregistrera donc bien la collection qu'il a reçue.la source
Je pense que les exemples de cet article de blog éclaireront davantage, car la question de savoir quel combo utiliser devient encore plus importante dans les scénarios de concurrence: importance de l'immuabilité pour la concurrence . Et pendant que nous y sommes, notez l'utilisation préférée de synchronized vs @volatile vs quelque chose comme AtomicReference: trois outils
la source
var immutable
contre.val mutable
En plus de nombreuses excellentes réponses à cette question. Voici un exemple simple, qui illustre les dangers potentiels de
val mutable
:Les objets mutables peuvent être modifiés à l'intérieur de méthodes, qui les prennent comme paramètres, tandis que la réaffectation n'est pas autorisée.
Résultat:
Une telle chose ne peut pas se produire avec
var immutable
, car la réaffectation n'est pas autorisée:Résulte en:
Étant donné que les paramètres de fonction sont traités,
val
cette réaffectation n'est pas autorisée.la source
mutable val
n'est pas possible avec leimmutable var
. Qu'est-ce qui est incorrect ici?+=
méthode comme le tampon de tableau. Vos réponses impliquent que ce+=
n'est pas la mêmex = x + y
chose. Votre déclaration selon laquelle les paramètres de fonction sont traités comme des vals est correcte et vous obtenez l'erreur que vous mentionnez, mais uniquement parce que vous l'avez utilisée=
. Vous pouvez obtenir la même erreur avec un ArrayBuffer, donc la mutabilité des collections ici n'est pas vraiment pertinente. Ce n'est donc pas une bonne réponse parce que ce n'est pas ce dont parle le PO. Bien que ce soit un bon exemple des dangers de transmettre une collection mutable si vous ne l'aviez pas l'intention de le faire.ArrayBuffer
en utilisantVector
. La question du PO est large, mais ils cherchaient des suggestions sur le moment de l'utiliser, donc je pense que ma réponse est utile car elle illustre les dangers de transmettre une collection mutable (le fait que celaval
n'aide pas);immutable var
est plus sûr quemutable val
.