Notez bien que cela vous convient, mais dans les langages fonctionnels comme Standard ML, tout est immuable par défaut. La mutation est prise en charge via un ref
type d'érence générique . Ainsi, une int
variable est immuable et une ref int
variable est un conteneur mutable pour int
s. Fondamentalement, les variables sont de vraies variables au sens mathématique (une valeur inconnue mais fixe) et ref
s sont des "variables" au sens impératif de la programmation - une cellule mémoire qui peut être écrite et lue. (J'aime les appeler assignables .)
Je pense que le problème const
est double. Tout d'abord, C ++ manque de garbage collection, ce qui est nécessaire pour avoir des structures de données persistantes non triviales . const
doit être profond pour avoir un sens, mais avoir des valeurs totalement immuables en C ++ n'est pas pratique.
Deuxièmement, en C ++, vous devez vous inscrire const
plutôt que de vous en retirer. Mais lorsque vous oubliez const
quelque chose et que vous le corrigez plus tard, vous vous retrouverez dans la situation «empoisonnement const» mentionnée dans la réponse de @ RobY où le const
changement se répercutera en cascade dans le code. Si const
c'était la valeur par défaut, vous ne vous retrouveriez pas à appliquer const
rétroactivement. De plus, devoir ajouter const
partout ajoute beaucoup de bruit au code.
Je soupçonne que les langages traditionnels qui ont suivi (par exemple Java) ont été fortement influencés par le succès et la façon de penser de C et C ++. Exemple: même avec la récupération de place, la plupart des API de collecte de langues supposent des structures de données mutables. Le fait que tout soit mutable et immuabilité est considéré comme un cas d'angle en dit long sur l'état d'esprit impératif derrière les langues populaires.
EDIT : Après réflexion sur commentaire de greenoldman, je me suis rendu compte qu'il const
ne s'agissait pas directement de l'immuabilité des données; const
encode dans le type de la méthode si elle a des effets secondaires sur l'instance.
Il est possible d'utiliser la mutation pour obtenir un comportement référentiellement transparent . Supposons que vous ayez une fonction qui, lorsqu'elle est appelée, renvoie successivement différentes valeurs - par exemple, une fonction qui lit un seul caractère stdin
. Nous pourrions utiliser le cache / mémoriser les résultats de cette fonction pour produire un flux de valeurs référentiellement transparent. Le flux serait une liste chaînée dont les nœuds appellent la fonction la première fois que vous essayez de récupérer leur valeur, mais mettent ensuite en cache le résultat. Donc, si stdin
constains Hello, world!
, la première fois que vous essayez de récupérer la valeur du premier nœud, il en lira unchar
et reviendra H
. Ensuite, il continuera à revenir H
sans autres appels pour lire a char
. De même, le second noeud lisait un char
destdin
la première fois que vous essayez de récupérer sa valeur, cette fois-ci en retournant e
et en mettant en cache ce résultat.
La chose intéressante ici est que vous avez transformé un processus intrinsèquement dynamique en un objet apparemment apatride. Cependant, il était nécessaire de muter l'état interne de l'objet (en mettant en cache les résultats) pour y parvenir - la mutation était un effet bénin . Il est impossible de faire notre CharStream
const
même si le flux se comporte comme une valeur immuable. Imaginez maintenant qu'il existe une Stream
interface avec des const
méthodes, et que toutes vos fonctions s'y attendent const Streams
. Votre CharStream
ne peut pas implémenter l'interface!
( EDIT 2: Apparemment, il y a un mot-clé C ++ appelé mutable
qui nous permettrait de tricher et de faireCharStream
const
Cependant, cette lacune Détruit. const
Les garanties s de - maintenant vous ne pouvez vraiment pas être sûr que quelque chose ne va pas muter par ses const
méthodes que je suppose que ce n'est pas. mauvais car vous devez explicitement demander l'échappatoire, mais vous êtes toujours complètement dépendant du système d'honneur.)
Deuxièmement, supposons que vous ayez des fonctions d'ordre élevé, c'est-à-dire que vous pouvez passer des fonctions comme arguments à d'autres fonctions. const
ness fait partie de la signature d'une fonction, vous ne pourrez donc pas passer des non- const
fonctions comme arguments aux fonctions qui attendentconst
fonctions. Une application aveugle const
entraînerait une perte de généralité.
Enfin, la manipulation d'un const
objet ne garantit pas qu'il ne mute pas un état externe (statique ou global) derrière votre dos, donc const
les garanties ne sont pas aussi fortes qu'elles le paraissent initialement.
Il n'est pas clair pour moi qu'encoder la présence ou l'absence d'effets secondaires dans le système de type est universellement une bonne chose.