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 reftype d'érence générique . Ainsi, une intvariable est immuable et une ref intvariable est un conteneur mutable pour ints. Fondamentalement, les variables sont de vraies variables au sens mathématique (une valeur inconnue mais fixe) et refs 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 constest 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 constplutôt que de vous en retirer. Mais lorsque vous oubliez constquelque 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 constchangement se répercutera en cascade dans le code. Si constc'était la valeur par défaut, vous ne vous retrouveriez pas à appliquer constrétroactivement. De plus, devoir ajouter constpartout 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 constne s'agissait pas directement de l'immuabilité des données; constencode 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 stdinconstains 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 Hsans autres appels pour lire a char. De même, le second noeud lisait un chardestdinla première fois que vous essayez de récupérer sa valeur, cette fois-ci en retournant eet 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 constmême si le flux se comporte comme une valeur immuable. Imaginez maintenant qu'il existe une Streaminterface avec des constméthodes, et que toutes vos fonctions s'y attendent const Streams. Votre CharStreamne peut pas implémenter l'interface!
( EDIT 2: Apparemment, il y a un mot-clé C ++ appelé mutablequi nous permettrait de tricher et de faireCharStream const Cependant, cette lacune Détruit. constLes garanties s de - maintenant vous ne pouvez vraiment pas être sûr que quelque chose ne va pas muter par ses constmé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. constness fait partie de la signature d'une fonction, vous ne pourrez donc pas passer des non- constfonctions comme arguments aux fonctions qui attendentconst fonctions. Une application aveugle constentraî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 constles 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.