Nota se questo è valido per te, ma in linguaggi funzionali come Standard ML tutto è immutabile per impostazione predefinita. La mutazione è supportata da un generico tipo ref erence. Quindi una variabile int è immutabile e una variabile ref int è un contenitore mutabile per int s. Fondamentalmente, le variabili sono reali variabili in senso matematico (un valore sconosciuto ma fisso) e ref s sono "variabili" nel senso di programmazione imperativo - una cella di memoria che può essere scritta e letta da . (Mi piace chiamarli assegnabili .)
Penso che il problema con const sia doppio. Innanzitutto, il C ++ manca della garbage collection, che è necessaria per avere strutture dati persistenti non banali. const deve essere profondi per avere senso, ma avere valori completamente immutabili in C ++ è poco pratico.
In secondo luogo, in C ++ è necessario optare per const piuttosto che rinunciare ad esso. Ma quando ti dimentichi di const qualcosa e successivamente lo aggiusti, finirai nella situazione di "avvelenamento da cost" menzionata nella risposta di @ RobY in cui la modifica di const si sovrapporrà a tutto il codice. Se const era il valore predefinito, non ti ritroverai ad applicare const in modo retroattivo. Inoltre, dover aggiungere const ovunque aggiunge molto rumore al codice.
Sospetto che i linguaggi mainstream che seguirono (ad esempio Java) fossero pesantemente influenzati dal successo e dal modo di pensare di C e C ++. Caso in questione, anche con la raccolta dei rifiuti Le API di raccolta della maggior parte dei linguaggi presuppongono strutture di dati mutabili. Il fatto che tutto sia mutevole e l'immutabilità è visto come un caso isolato parla della mentalità imperativa dietro le lingue popolari.
EDIT : dopo aver riflettuto su commento di greenoldman Mi sono reso conto che const non riguarda direttamente l'immutabilità dei dati; const codifica nel tipo del metodo indipendentemente dal fatto che abbia effetti collaterali sull'istanza.
È possibile utilizzare la mutazione per ottenere il comportamento referenzialmente trasparente . Supponiamo di avere una funzione che, quando viene chiamata, restituisce successivamente valori diversi, ad esempio una funzione che legge un singolo carattere da stdin . Potremmo usare cache / memoize dei 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 recuperarne il valore, ma in seguito il risultato verrà memorizzato nella cache. Quindi se stdin contiene Hello, world! , la prima volta che provi a recuperare il valore del primo nodo, leggerà un char e restituirà H . Successivamente continuerà a restituire H senza ulteriori chiamate per leggere char . Allo stesso modo, il secondo nodo leggerà un char da stdin la prima volta che proverai a recuperare il suo valore, questa volta restituendo e e memorizzando il risultato in cache.
La cosa interessante qui è che hai trasformato un processo intrinsecamente statico in un oggetto apparentemente senza stato. Tuttavia, per ottenere questo risultato era necessario mutare lo stato interno dell'oggetto (memorizzando nella cache i risultati) - la mutazione era un effetto benigno . È impossibile rendere il nostro CharStream const anche se lo stream si comporta come un valore immutabile. Ora immagina ci sia un'interfaccia Stream con metodi const , e tutte le tue funzioni prevedono const Streams . Il tuo CharStream non può implementare l'interfaccia!
( EDIT 2: Apparentemente c'è una parola chiave C ++ chiamata mutable che ci consentirebbe di imbrogliare e rendere CharStream const . Tuttavia, questa scappatoia distrugge le garanzie di const - ora non puoi davvero essere sicuro che qualcosa vince 'm mutare attraverso i suoi metodi const . Suppongo che non sia poi così male dal momento che devi richiedere esplicitamente la scappatoia, ma sei ancora completamente dipendente dal sistema d'onore.)
In secondo luogo, si supponga di avere funzioni di ordine elevato, ovvero, è possibile passare funzioni come argomenti ad altre funzioni. const ness fa parte della firma di una funzione, quindi non potresti passare le funzioni non- const come argomenti alle funzioni che aspettano const funzioni. L'imposizione ciecamente di const qui comporterebbe una perdita di generalità.
Infine, la manipolazione di un oggetto const non garantisce che non stia modificando uno stato esterno (statico o globale) dietro la schiena, quindi le garanzie di const non sono così forti come appaiono inizialmente.
Non è chiaro per me che codificare la presenza o l'assenza di effetti collaterali nel sistema dei tipi sia universalmente una buona cosa.