Tenga en cuenta que si esto califica para usted, pero en lenguajes funcionales como Standard ML, todo es inmutable de manera predeterminada. La mutación es compatible a través de un ref
tipo de diferencia genérico . Entonces, una int
variable es inmutable, y una ref int
variable es un contenedor mutable para int
s. Básicamente, las variables son variables reales en el sentido matemático (un valor desconocido pero fijo) y las ref
s son "variables" en el sentido de programación imperativo, una celda de memoria en la que se puede escribir y leer. (Me gusta llamarlos asignables ).
Creo que el problema const
es doble. Primero, C ++ carece de recolección de basura, lo cual es necesario para tener estructuras de datos persistentes no triviales . const
debe ser profundo para que tenga sentido, pero tener valores totalmente inmutables en C ++ no es práctico.
En segundo lugar, en C ++ debe optar por participar en const
lugar de rechazarlo. Pero cuando se olvida de const
algo y luego lo arregla, terminará en la situación de "envenenamiento constante" mencionada en la respuesta de @ RobY, donde el const
cambio caerá en cascada a lo largo del código. Si const
fuera el predeterminado, no se encontraría aplicando const
retroactivamente. Además, tener que agregar en const
todas partes agrega mucho ruido al código.
Sospecho que los lenguajes principales que siguieron (por ejemplo, Java) fueron fuertemente moldeados por el éxito y la forma de pensar de C y C ++. Caso en cuestión, incluso con la recolección de basura, la mayoría de las API de recolección de idiomas asumen estructuras de datos mutables. El hecho de que todo sea mutable y la inmutabilidad se vea como un caso de esquina habla mucho sobre la mentalidad imperativa detrás de los lenguajes populares.
EDITAR : Después de reflexionar sobre el comentario de Greenoldman, me di cuenta de que const
no se trata directamente de la inmutabilidad de los datos; const
codifica en el tipo de método si tiene efectos secundarios en la instancia.
Es posible usar la mutación para lograr un comportamiento referencialmente transparente . Suponga que tiene una función que, cuando se llama sucesivamente, devuelve valores diferentes, por ejemplo, una función que lee un solo carácter stdin
. Podríamos usar caché / memorizar los resultados de esta función para producir un flujo de valores referencialmente transparente. La secuencia sería una lista vinculada cuyos nodos llamarán a la función la primera vez que intente recuperar su valor, pero luego almacenar en caché el resultado. Entonces, si stdin
constains Hello, world!
, la primera vez que intente recuperar el valor del primer nodo, leerá uno char
y regresará H
. Luego continuará regresando H
sin más llamadas para leer a char
. Del mismo modo, el segundo nodo leería una char
destdin
la primera vez que intente recuperar su valor, esta vez devuelve e
y almacena en caché ese resultado.
Lo interesante aquí es que ha convertido un proceso inherentemente con estado en un objeto que aparentemente no tiene estado. Sin embargo, fue necesario mutar el estado interno del objeto (almacenando en caché los resultados) para lograr esto: la mutación fue un efecto benigno . Es imposible hacer nuestro CharStream
const
a pesar de que la transmisión se comporta como un valor inmutable. Ahora imagine que hay una Stream
interfaz con const
métodos y que todas sus funciones esperan const Streams
. ¡No CharStream
puedes implementar la interfaz!
( EDITAR 2: Aparentemente hay una palabra clave de C ++ llamada mutable
que nos permitiría hacer trampa y hacerCharStream
const
. Sin embargo, esta escapatoria destruye const
las garantías, ahora realmente no puedes estar seguro de que algo no mutará a través de sus const
métodos. Supongo que no es eso malo, ya que debe solicitar explícitamente la escapatoria, pero aún depende completamente del sistema de honor).
En segundo lugar, suponga que tiene funciones de orden superior, es decir, puede pasar funciones como argumentos a otras funciones. const
ness es parte de la firma de una función, por lo que no podrá pasar las no const
funciones como argumentos a las funciones que esperan const
funciones. Hacer cumplir ciegamente const
aquí conduciría a una pérdida de generalidad.
Finalmente, manipular un const
objeto no garantiza que no esté mutando algún estado externo (estático o global) a sus espaldas, por lo que const
las garantías no son tan fuertes como parecen inicialmente.
Para mí no está claro que codificar la presencia o ausencia de efectos secundarios en el sistema de tipos sea universalmente una buena cosa.