La inmutabilidad o la mutabilidad no son conceptos que tengan sentido en la programación funcional.
El contexto computacional.
Esta es una muy buena pregunta que es un seguimiento interesante (no un duplicado) de otra reciente: ¿Cuál es la diferencia entre asignación, valoración y enlace de nombre?
En lugar de responder a sus declaraciones una por una, estoy tratando de darle una descripción estructurada de lo que está en juego.
Hay varios asuntos a considerar para responderle, que incluyen:
El estilo de programación funcional parece tonto porque lo ves con un ojo de programador imperativo. Pero es un paradigma diferente, y sus conceptos y percepción imperativos son extraños, fuera de lugar. Los compiladores no tienen tales prejuicios.
Pero la conclusión final es que es posible escribir programas de una manera puramente funcional, incluso para el aprendizaje automático, aunque la programación funcional no tiene el concepto de almacenar datos. Parece que no estoy de acuerdo en este punto con otras respuestas.
En la esperanza, algunos estarán interesados a pesar de la extensión de esta respuesta.
Paradigmas computacionales
La pregunta es sobre la programación funcional (también conocida como programación aplicativa), un modelo específico de computación, cuyo representante teórico y más simple es el cálculo lambda.
Si se mantiene en un nivel teórico, hay muchos modelos de computación: la máquina de Turing (TM), la máquina de RAM y otros , el cálculo lambda, la lógica combinatoria, la teoría de funciones recursivas, los sistemas semi-Thue, etc. Se ha demostrado que los modelos son equivalentes en términos de lo que pueden abordar, y esa es la esencia de la
tesis de Church-Turing .
Un concepto importante es reducir los modelos entre sí, que es la base para establecer las equivalencias que conducen a la tesis de Church-Turing. Visto desde la perspectiva de los programadores, reducir un modelo a otro es más o menos lo que generalmente se llama un compilador. Si toma la programación lógica como su modelo de computación, es bastante diferente del modelo proporcionado por la PC que compró en una tienda, y el compilador traduce los programas escritos en lenguaje de programación lógica al modelo computacional representado por su PC (más o menos la computadora RAM).
β
En la práctica, los lenguajes de programación que usamos tienden a mezclar conceptos de diferentes orígenes teóricos, tratando de hacerlo para que partes seleccionadas de un programa puedan beneficiarse de las propiedades de algún modelo cuando sea apropiado. Del mismo modo, las personas que construyen sistemas pueden elegir diferentes idiomas para diferentes componentes, para adaptarse mejor al idioma para la tarea en cuestión.
Por lo tanto, rara vez ve un paradigma de programación en estado puro en un lenguaje de programación. Los lenguajes de programación todavía se clasifican de acuerdo con el paradigma dominante, pero las propiedades del lenguaje pueden verse afectadas cuando se involucran conceptos de otros paradigmas, a menudo borrando las distinciones y los problemas conceptuales.
Por lo general, los lenguajes como Haskell y ML o CAML se consideran funcionales, pero pueden permitir un comportamiento imperativo ... De lo contrario, ¿por qué uno hablaría del " subconjunto puramente funcional "?
Entonces uno puede afirmar que puede hacer esto o aquello en mi lenguaje de programación funcional, pero en realidad no responde una pregunta sobre programación funcional cuando se basa en lo que se puede considerar extra funcional.
Las respuestas deberían estar más precisamente relacionadas con un paradigma específico, sin los extras.
¿Qué es una variable?
Otro problema es el uso de la terminología. En matemáticas, una variable es una entidad que representa un valor indeterminado en algún dominio. Se utiliza para diversos fines. Utilizado en una ecuación, puede representar cualquier valor tal que se verifique la ecuación. Esta visión se utiliza en la programación lógica bajo el nombre de " variable lógica ", probablemente porque la variable nombre ya tenía otro significado cuando se desarrolló la programación lógica.
En la programación imperativa tradicional, una variable se entiende como algún tipo de contenedor (o ubicación de memoria) que puede memorizar la representación de un valor y posiblemente reemplazar su valor actual por otro).
En la programación funcional, una variable tiene el mismo propósito que tiene en matemáticas como un marcador de posición para algún valor, aún no se ha proporcionado. En la programación imperativa tradicional, este papel es desempeñado por la constante (no debe confundirse con literales cuyo valor determinado se expresa con una notación específica de ese dominio de valores, como 123, verdadero, ["abdcz", 3.14]).
Las variables de cualquier tipo, así como las constantes, pueden representarse mediante identificadores.
La variable imperativa puede cambiar su valor y esa es la base de la mutabilidad. La variable funcional no puede.
Los lenguajes de programación generalmente permiten construir entidades más grandes a partir de las más pequeñas en el lenguaje.
Los lenguajes imperativos permiten que tales construcciones incluyan variables y eso es lo que le da datos mutables.
Como leer un programa
Un programa es fundamentalmente una descripción abstracta de su algoritmo, es un lenguaje, ya sea un diseño pragmático o un lenguaje paradigmáticamente puro.
En principio, puede tomar cada declaración de lo que se supone que significa abstractamente. Luego, el compilador lo traducirá a una forma apropiada para que la computadora lo ejecute, pero ese no es su problema en primera aproximación.
Por supuesto, la realidad es un poco más dura, y a menudo es bueno tener una idea de lo que sucede para evitar estructuras que el compilador no sabrá cómo manejar para una ejecución eficiente. Pero eso ya es optimización ... para qué compiladores puede ser muy bueno, a menudo mejor que los programadores.
Programación funcional y mutabilidad.
La mutabilidad se basa en la existencia de variables imperativas que pueden contener valores, que se cambiarán por asignación. Como estos no existen en la programación funcional, todo puede verse como inmutable.
La programación funcional se ocupa exclusivamente de valores.
Sus primeras cuatro afirmaciones sobre la inmutabilidad son en su mayoría correctas, pero describen con visión imperativa algo que no es imperativo. Es un poco como describir con colores en un mundo donde todos son ciegos. Está utilizando conceptos ajenos a la programación funcional.
Solo tiene valores puros, y una matriz de enteros es un valor puro. Para obtener otra matriz que difiera solo en un elemento, debe usar un valor de matriz diferente. Cambiar un elemento es solo un concepto que no existe en este contexto. Puede tener una función que tiene una matriz y algunos índices como argumento, y devuelve un resultado que es una matriz casi idéntica que difiere solo donde lo indican los índices. Pero sigue siendo un valor de matriz independiente. Cómo se representan estos valores no es su problema. Tal vez ellos "comparten" mucho en la traducción imperativa para la computadora ... pero ese es el trabajo del compilador ... y usted ni siquiera quiere saber para qué tipo de arquitectura de máquina está compilando.
No copie valores (no tiene sentido, es un concepto extraño). Solo usa valores que existen en los dominios que ha definido en su programa. O los describe (como literales) o son el resultado de aplicar una función a otros valores. Puede asignarles un nombre (definiendo así una constante) para asegurarse de que se use el mismo valor en diferentes lugares del programa. Tenga en cuenta que la aplicación de la función no debe percibirse como un cálculo sino como el resultado de la aplicación a los argumentos dados. Escribir 5+2
o escribir 7
equivale a lo mismo. Lo cual es consistente con el párrafo anterior.
No hay variables imperativas. Ninguna asignación es posible. Puede vincular nombres solo a valores (para formar constantes), a diferencia de los lenguajes imperativos donde puede vincular nombres a variables asignables.
Si eso tiene un costo en complejidad no está totalmente claro. Por un lado, la referencia a la complejidad se refiere a paradigmas imperativos. No se define como tal para la programación funcional, a menos que elija leer su programa funcional como un imperativo, que no es la intención del diseñador. De hecho, la vista funcional está pensada para que no se preocupe por tales problemas y se concentre en lo que se está calculando. Es un poco como especificación versus implementación.
El compilador debe ocuparse de la implementación y ser lo suficientemente inteligente como para adaptar lo que se debe hacer al hardware que lo hará, sea lo que sea.
No digo que los programadores nunca se preocupen por eso. Tampoco estoy diciendo que los lenguajes de programación y la tecnología de compilación sean tan maduros como quisiéramos que fueran.
Respondiendo las preguntas
No modifica el valor existente (concepto alienígena), pero calcula nuevos valores que difieren donde se desee, posiblemente al tener un elemento adicional, es una lista.
El programa puede obtener nuevos datos. El punto es cómo expresas eso en el idioma. Por ejemplo, puede considerar que el programa funciona con un valor específico, posiblemente de tamaño ilimitado, que se denomina flujo de entrada. Es un valor que se supone que debe estar sentado allí (ya sea que ya se conozca por completo o no, no es su problema). Luego tiene una función que devuelve un par compuesto por el primer elemento de la secuencia y el resto de la secuencia.
Puede usar eso para construir redes de componentes de comunicación de una manera puramente aplicativa (corutinas)
El aprendizaje automático es solo otro problema cuando tiene que aumentar datos y modificar valores. En la programación funcional no hace eso: solo calcula nuevos valores que difieren adecuadamente de acuerdo con los datos de entrenamiento. La máquina resultante también funcionará. Lo que le preocupa es calcular el tiempo y la eficiencia del espacio. Pero, nuevamente, ese es un tema diferente, que idealmente debería ser tratado por el compilador.
Observaciones finales
Está bastante claro, a partir de los comentarios u otras respuestas, que los lenguajes prácticos de programación funcional no son puramente funcionales. Eso es una reflexión sobre el hecho de que nuestra tecnología aún debe mejorarse, especialmente en lo que respecta a la compilación.
¿Es posible escribir en un estilo puramente aplicativo? La respuesta se conoce desde hace unos 40 años y es "sí". El mismo propósito de la semántica denotacional, tal como apareció en la década de 1970, era precisamente traducir (compilar) lenguajes a un estilo puramente funcional, considerado mejor matemáticamente y, por lo tanto, considerado una mejor base para definir la semántica de los programas.
El aspecto interesante de esto es que la estructura de programación imperativa, incluidas las variables, se puede traducir a un estilo funcional mediante la introducción de dominios de valores apropiados, como un almacén de datos. Y a pesar del estilo funcional, sigue siendo sorprendentemente similar al código de los compiladores reales escritos en un estilo imperativo.