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.