Nota se questo è adatto a te, ma in linguaggi funzionali come Standard ML tutto è immutabile per impostazione predefinita. La mutazione è supportata attraverso un reftipo di erenza generico . Quindi una intvariabile è immutabile e una ref intvariabile è un contenitore mutabile per ints. Fondamentalmente, le variabili sono variabili reali in senso matematico (un valore sconosciuto ma fisso) e refs sono "variabili" in senso imperativo di programmazione - una cella di memoria che può essere scritta e da cui leggere. (Mi piace chiamarli assegnabili .)
Penso che il problema constsia duplice. Innanzitutto, il C ++ manca della garbage collection, necessaria per avere strutture di dati persistenti non banali . const deve essere profondo per avere un senso, ma avere valori completamente immutabili in C ++ è impraticabile.
In secondo luogo, in C ++ è necessario optare constpiuttosto che non. Ma quando ti dimentichi di constqualcosa e in seguito lo risolvi, finirai nella situazione di "avvelenamento da costanti" menzionata nella risposta di @ RobY in cui il constcambiamento si ripercuoterà su tutto il codice. Se constfosse l'impostazione predefinita, non ti ritroveresti ad applicare constretroattivamente. Inoltre, dover aggiungere constovunque aggiunge molto rumore al codice.
Sospetto che i linguaggi tradizionali che seguirono (ad es. Java) furono fortemente modellati dal successo e dal modo di pensare di C e C ++. Caso in questione, anche con la raccolta dei rifiuti, le API della maggior parte delle lingue assumono strutture di dati mutabili. Il fatto che tutto sia mutevole e l'immutabilità è visto come un caso esemplare che parla a lungo della mentalità imperativa dietro le lingue popolari.
EDIT : Dopo aver riflettuto sul commento di Greenoldman mi sono reso conto che constnon riguarda direttamente l'immutabilità dei dati; constcodifica nel tipo di metodo se ha effetti collaterali sull'istanza.
È possibile utilizzare la mutazione per ottenere un comportamento referenzialmente trasparente . Supponiamo di avere una funzione che, quando chiamata, restituisce in successione valori diversi, ad esempio una funzione che legge un singolo carattere da stdin. Potremmo usare cache / memoize i risultati di questa funzione per produrre un flusso di valori referenzialmente trasparente. Lo stream sarebbe un elenco collegato i cui nodi chiameranno la funzione la prima volta che si tenta di recuperare il loro valore, ma quindi memorizzare nella cache il risultato. Quindi, se il stdinproblema persiste Hello, world!, la prima volta che provi a recuperare il valore del primo nodo, ne leggerà uno chare tornerà H. Successivamente continuerà a tornare Hsenza ulteriori chiamate per leggere a char. Allo stesso modo, il secondo nodo avrebbe letto un chardastdinla prima volta che provi a recuperarne il valore, questa volta restituisce ee memorizza nella cache quel risultato.
La cosa interessante qui è che hai trasformato un processo intrinsecamente stato in un oggetto apparentemente senza stato. Tuttavia, è stato necessario mutare lo stato interno dell'oggetto (memorizzando nella cache i risultati) per raggiungere questo obiettivo: la mutazione era un effetto benigno . È impossibile rendere il nostro CharStream constanche se il flusso si comporta come un valore immutabile. Ora immagina che ci sia Streamun'interfaccia con i constmetodi e tutte le tue funzioni si aspettano const Streams. Il tuo CharStreamnon può implementare l'interfaccia!
( EDIT 2: Apparentemente c'è una parola chiave C ++ chiamata mutableche ci permetterebbe di imbrogliare e creareCharStream const . Tuttavia, questa scappatoia distrugge constle garanzie - ora non puoi davvero essere sicuro che qualcosa non muterà attraverso i suoi constmetodi. Suppongo che non sia così male dal momento che è necessario richiedere esplicitamente la scappatoia, ma si fa ancora completamente affidamento sul sistema d'onore.)
In secondo luogo, supponiamo di avere funzioni di ordine elevato, ovvero è possibile passare funzioni come argomenti ad altre funzioni. constness fa parte della firma di una funzione, quindi non si sarebbe in grado di passare le non constfunzioni come argomenti alle funzioni che si aspettano constfunzioni. Applicare ciecamente constqui porterebbe a una perdita di generalità.
Infine, manipolare un constoggetto non garantisce che non stia mutando uno stato esterno (statico o globale) alle tue spalle, quindi constle garanzie non sono così forti come sembrano inizialmente.
Non mi è chiaro che codificare la presenza o l'assenza di effetti collaterali nel sistema dei tipi sia universalmente una buona cosa.