Quiero entender a un nivel bajo, ¿qué sucedería si la estructura de datos no es persistente?
Veamos un generador de números pseudoaleatorios con un gran espacio de estado (como " Mersenne twister " con un estado de 2450 bytes) como una estructura de datos. Realmente no queremos usar ningún número aleatorio más de una vez, por lo que parece haber pocas razones para implementar esto como una estructura de datos persistente inmutable. Ahora preguntémonos qué podría salir mal en el siguiente código:
mt_gen = CreateMersenneTwisterPRNGen(seed)
integral = MonteCarloIntegral_Bulk(mt_gen) + MonteCarloIntegral_Boundary(mt_gen)
La mayoría de los lenguajes de programación no especifican el orden en el que MonteCarloIntegral_Bulk
y MonteCarloIntegral_Boundary
serán evaluados. Si ambos toman una referencia a un mt_gen mutable como argumento, el resultado de este cálculo puede depender de la plataforma. Peor aún, puede haber plataformas en las que el resultado no sea reproducible en absoluto entre diferentes ejecuciones.
Se puede diseñar una estructura de datos mutable eficiente para mt_gen de modo que cualquier entrelazado de la ejecución de MonteCarloIntegral_Bulk
y MonteCarloIntegral_Boundary
dé un resultado "correcto", pero un entrelazado diferente en general dará lugar a un resultado "correcto" diferente. Esta no reproducibilidad hace que la función correspondiente sea "impura" y también conduce a otros problemas.
La no reproducibilidad puede evitarse imponiendo un orden de ejecución secuencial fijo. Pero en ese caso, el código podría organizarse de tal manera que solo una única referencia a mt_gen esté disponible en un momento dado. En un lenguaje de programación funcional mecanografiado, los tipos de unicidad podrían usarse para hacer cumplir esta restricción, permitiendo así actualizaciones seguras mutables también en el contexto de lenguajes de programación funcional puros. Todo esto puede sonar agradable y elegante, pero al menos en teoría las simulaciones de Monte Carlo son vergonzosamente paralelas, y nuestra "solución" acaba de destruir esta propiedad. Este no es solo un problema teórico, sino un problema práctico muy real. Sin embargo, tenemos que modificar (la funcionalidad que ofrece) nuestro generador de números pseudoaleatorios y la secuencia de números aleatorios que produce, y ningún lenguaje de programación puede hacer esto automáticamente por nosotros. (Por supuesto, podemos usar una biblioteca de números pseudoaleatoria diferente que ya ofrece la funcionalidad requerida).
En un nivel bajo, las estructuras de datos mutables conducen fácilmente a la no reproducibilidad (y, por lo tanto, a la impureza), si el orden de ejecución no es secuencial y fijo. Una estrategia imperativa típica para tratar estos problemas es tener fases secuenciales con un orden de ejecución fijo, durante el cual se cambian las estructuras de datos mutables, y fases paralelas con un orden de ejecución arbitrario, durante el cual todas las estructuras de datos mutables compartidas permanecen constantes.
Andrej Bauer planteó la cuestión del alias para estructuras de datos mutables. Curiosamente, diferentes lenguajes imperativos como Fortran y C tienen diferentes supuestos sobre el alias permitido de argumentos de función, y la mayoría de los programadores desconocen que su lenguaje tiene un modelo de alias.
La inmutabilidad y la semántica del valor pueden estar ligeramente sobrevaloradas. Lo que es más importante es que el sistema de tipos y el marco lógico (como el modelo de máquina abstracto, el modelo de alias, el modelo de concurrencia o el modelo de administración de memoria) de su lenguaje de programación ofrece suficiente soporte para trabajar de manera "segura" con datos "eficientes" estructuras La introducción de la "semántica de movimiento" a C ++ 11 podría parecer un gran paso atrás en términos de pureza y "seguridad" desde un punto de vista teórico, pero en la práctica es todo lo contrario. El sistema de tipos y el marco lógico del lenguaje se han ampliado para eliminar grandes partes del peligro asociado a la nueva semántica. (E incluso si quedan bordes ásperos, esto no significa que esto no pueda mejorarse con un "mejor"
Uday Reddy planteó la cuestión de que las matemáticas nunca funcionaron con objetos de datos mutables, y que los sistemas de tipos para programas funcionales están bien desarrollados para objetos de datos inmutables. Esto me recordó la explicación de Jean-Yves Girard de que las matemáticas no están acostumbradas a trabajar con objetos cambiables, cuando trata de motivar la lógica lineal.
Uno podría preguntarse cómo ampliar el sistema de tipos y el marco lógico de los lenguajes de programación funcionales para permitir trabajar de manera "segura" con estructuras de datos no persistentes "eficientes" mutables. Un problema aquí podría ser que la lógica clásica y las álgebras booleanas podrían no ser el mejor marco lógico para trabajar con estructuras de datos mutables. ¿Quizás la lógica lineal y los monoides conmutativos podrían ser más adecuados para esa tarea? ¿Quizás debería leer lo que Philip Wadler tiene que decir sobre lógica lineal como sistema de tipos para lenguajes de programación funcionales? Pero incluso si la lógica lineal no fuera capaz de resolver este problema, esto no significa que el sistema de tipos y el marco lógico de un lenguaje de programación funcional no puedan extenderse para permitir "seguro" y "eficiente"