haskell - tutorial - ¿Qué significa "puro" en "lenguaje funcional puro"?
learn you a haskell pdf (7)
"Puro" y "funcional" son dos conceptos separados, aunque uno no es muy útil sin el otro.
Una expresión pura es idempotente: se puede evaluar cualquier cantidad de veces, con resultados idénticos cada vez. Esto significa que la expresión no puede tener ningún efecto secundario observable. Por ejemplo, si una función mutó sus argumentos, estableció una variable en algún lugar o cambió el comportamiento basándose en algo que no sea solo su entrada, entonces esa llamada a la función no es pura.
Un lenguaje de programación funcional es aquel en que las funciones son de primera clase. En otras palabras, puede manipular funciones con exactamente la misma facilidad con la que puede manipular todos los demás valores de primera clase. Por ejemplo, ser capaz de usar una "función que devuelve bool" como una "estructura de datos que representa un conjunto" sería fácil en un lenguaje de programación funcional.
La programación en lenguajes de programación funcionales a menudo se realiza en un estilo mayoritariamente puro, y es difícil ser estrictamente puro sin manipulación de funciones de orden superior habilitada por lenguajes de programación funcionales.
Haskell es un lenguaje de programación funcional, en el que (casi) todas las expresiones son puras; por lo tanto, Haskell es un lenguaje de programación puramente funcional.
Haskell ha sido llamado un "lenguaje funcional puro".
¿Qué significa "puro" en este contexto? ¿Qué consecuencias tiene esto para un programador?
Amr Sabry escribió un paper sobre lo que es un lenguaje funcional puro. Haskell es, por esta definición, considerada pura, si ignoramos cosas como InseguroPerformIO. Usar esta definición también hace que ML y Erlang sean impuros. Hay subconjuntos de la mayoría de los idiomas que califican como puros, pero personalmente no creo que sea muy útil hablar de que C es un lenguaje puro.
El orden superior es ortogonal a la pureza, puede diseñar un lenguaje funcional puro de primer orden.
Como no puede haber efectos secundarios en el código funcional puro, las pruebas se vuelven mucho más fáciles ya que no hay un estado externo para verificar o verificar. Además, debido a esto, extender el código puede ser más fácil.
Perdí la cuenta de la cantidad de veces que tuve problemas con efectos secundarios no obvios al extender / corregir (o tratar de corregir) el código.
Como otros han mencionado, el término "puro" en "lenguaje de programación puramente funcional" se refiere a la falta de efectos secundarios observables. Para mí, esto lleva directamente a la pregunta:
¿Qué es un efecto secundario?
He visto los efectos secundarios explicados tanto como
- algo que hace una función que no sea simplemente calcular su resultado
- algo que puede afectar el resultado de una función que no sean las entradas a la función.
Si la primera definición es la correcta, entonces no se puede decir que ninguna función que haga I / O (por ejemplo, escribir en un archivo) sea una función "pura". Mientras que los programas Haskell pueden llamar a funciones que causan que se realice E / S, parece que Haskell no es un lenguaje de programación puramente funcional (como se dice) de acuerdo con esta definición.
Por esta y otras razones, creo que la segunda definición es la más útil. De acuerdo con la segunda definición, Haskell todavía puede afirmar que es un lenguaje de programación completamente puro porque las funciones que hacen que las E / S se lleven a cabo calculan resultados basados únicamente en entradas de funciones. La forma en que Haskell concilia estos requisitos aparentemente contradictorios es bastante interesante, creo, pero resistiré la tentación de desviarme un poco más de la respuesta a la pregunta real.
En un lenguaje funcional puro , no puedes hacer nada que tenga un efecto secundario.
Un efecto secundario significaría que evaluar una expresión cambia algún estado interno que luego haría que la evaluación de la misma expresión tenga un resultado diferente. En un lenguaje funcional puro, puede evaluar la misma expresión tantas veces como quiera con los mismos argumentos, y siempre devolverá el mismo valor, porque no hay ningún estado para cambiar.
Por ejemplo, un lenguaje funcional puro no puede tener un operador de asignación o hacer entrada / salida, aunque para fines prácticos, incluso los lenguajes funcionales puros a menudo llaman bibliotecas impuras para hacer E / S.
Estrictamente hablando, un lenguaje funcional puro es un lenguaje funcional (es decir, un lenguaje donde las funciones son valores de primera clase) donde las expresiones no tienen efectos secundarios. El término "lenguaje puramente funcional" es sinónimo.
Según esta definición, Haskell no es un lenguaje funcional puro. Cualquier lenguaje en el que pueda escribir programas que muestren su resultado, lean y escriban archivos, tengan una GUI, etc., no es puramente funcional. Por lo tanto, ningún lenguaje de programación de propósito general es puramente funcional (pero existen lenguajes funcionalmente específicos de dominio útiles: típicamente se pueden ver como lenguajes incrustados de alguna manera).
Hay un sentido relajado y útil en el que lenguajes como Haskell y Erlang pueden considerarse puramente funcionales, pero idiomas como ML y Scheme no pueden. Un lenguaje puede considerarse puramente funcional si existe un subconjunto razonablemente grande, útil y bien caracterizado en el que los efectos secundarios son imposibles. Por ejemplo, en Haskell, todos los programas cuyo tipo no está construido a partir de IO
u otra mónada que denote un efecto son libres de efectos secundarios. En Erlang, todos los programas que no usan librerías IO-inducing o características de simultaneidad están libres de efectos secundarios (esto es más de una extensión que el caso de Haskell). Por el contrario, en ML o Scheme, un efecto secundario puede ser enterrado en cualquier función.
Desde esta perspectiva, el subconjunto puramente funcional de Haskell puede verse como el lenguaje incorporado para tratar el comportamiento dentro de cada mónada (por supuesto, esta es una perspectiva extraña, ya que casi todo el cálculo está sucediendo en este subconjunto "incrustado"), y el subconjunto puramente funcional de Erlang se puede ver como el lenguaje incrustado sí trata con el comportamiento local.
Graham Hutton tiene una perspectiva ligeramente diferente y bastante interesante sobre el tema de los lenguajes puramente funcionales :
Algunas veces, el término "puramente funcional" también se usa en un sentido más amplio para significar lenguajes que podrían incorporar efectos computacionales, pero sin alterar la noción de "función" (como lo demuestra el hecho de que se preservan las propiedades esenciales de las funciones). , la evaluación de una expresión puede generar una ''tarea'', que luego se ejecuta por separado para causar efectos computacionales. Las fases de evaluación y ejecución están separadas de tal forma que la fase de evaluación no compromete las propiedades estándar de las expresiones y funciones. Los mecanismos de entrada / salida de Haskell, por ejemplo, son de este tipo.
Es decir, en Haskell, una función tiene el tipo a -> b
y no puede tener efectos secundarios. Una expresión de tipo IO (a -> b)
puede tener efectos secundarios, pero no es una función. Por lo tanto, en Haskell las funciones deben ser puras, por lo que Haskell es puramente funcional.
Una función pura es aquella que no tiene efectos secundarios: toma un valor y devuelve un valor. No hay un estado global que modifique las funciones. Un lenguaje funcional puro es aquel que fuerza a las funciones a ser puras. La pureza tiene varias consecuencias interesantes, como el hecho de que la evaluación puede ser floja, ya que una llamada a función no tiene otro propósito que devolver un valor, entonces no es necesario que ejecutes la función si no vas a utilizarla es valioso. Gracias a esto, cosas como las funciones recursivas en listas infinitas son comunes en Haskell.
Otra consecuencia es que no importa en qué orden se evalúan las funciones, ya que no pueden afectarse entre sí, puede hacerlo en el orden que le resulte conveniente. Esto significa que algunos de los problemas planteados por la programación paralela simplemente no existen, ya que no hay un orden "incorrecto" o "correcto" para que las funciones se ejecuten.