STDOUT y su impureza

10

He leído muchos libros y artículos sobre programación funcional y todavía me avergüenzo de no poder entender con seguridad algunos conceptos muy básicos.

Una de las ideas principales de la programación funcional es que la misma entrada siempre debe producir la misma salida. Por lo tanto, por ejemplo, la consulta de la base de datos o la escritura de un archivo no se puede hacer en un estilo funcional puro por definición. Esa es, por ejemplo, una de las razones por las que necesitamos mónadas.

La pregunta es: ¿por qué consideramos la salida STDOUT como algo impuro? Sí, cualquier controlador de archivos es arriesgado: nunca podemos estar seguros de que los datos siempre se escribirán. ¿Pero qué hay de STDOUT? ¿Por qué deberíamos considerarlo como algo poco confiable? ¿Es más poco confiable esa evaluación en sí misma? Quiero decir, siempre podemos apretar el gatillo y, por lo tanto, interrumpir el cálculo.

shabunc
fuente

Respuestas:

6

Por lo tanto, por ejemplo, la consulta de la base de datos o la escritura de un archivo no se puede hacer en un estilo funcional puro por definición. Esa es, por ejemplo, una de las razones por las que necesitamos mónadas.

Nadie "necesita" mónadas, esa es solo una forma de describir las cosas. De hecho, probablemente ni siquiera sea la mejor manera. Algunas formas de tipificación de efectos , tipos de unicidad o un sistema basado en una lógica lineal completa parecen más persuasivos en teoría, pero son desviaciones más radicales de los sistemas de tipos conocidos y más complicados de expresar. Monadic IO, como se encuentra en Haskell, es un compromiso entre usabilidad y simplicidad, ya que esencialmente modela una programación totalmente imperativa de una manera que coexistía fácilmente con el sistema de tipo ML existente que ya se usa en el lenguaje.

La pregunta es: ¿por qué consideramos la salida STDOUT como algo impuro? Sí, cualquier controlador de archivos es arriesgado: nunca podemos estar seguros de que los datos siempre se escribirán. ¿Pero qué hay de STDOUT? ¿Por qué deberíamos considerarlo como algo poco confiable? ¿Es más poco confiable esa evaluación en sí misma? Quiero decir, siempre podemos apretar el gatillo y, por lo tanto, interrumpir el cálculo.

No lo es, y nosotros no. La entrada y salida del programa en su conjunto puede considerarse simplemente como argumentos y resultados del tratamiento de todo el programa como una función pura grande. Mientras imprima lo mismo en stdout si lo alimenta desde stdin, sigue siendo una función pura. De hecho, antes de introducir la E / S monádica, Haskell usó un sistema de E / S basado en flujo que usaba flujos perezosos puros para entrada y salida. Lo dejó caer porque aparentemente era un dolor de usar, lo que puede darle una idea de por qué no ha oído hablar de algo así. :]

Para hacer el punto de una manera más tonta, considere el lenguaje esotérico minimalista, Lazy K :

Lazy K es un lenguaje de programación funcional de recolección de basura, referencialmente transparente, con un simple sistema de E / S basado en flujo.

Lo que distingue a Lazy K de otros lenguajes es su falta casi total de otras características. No ofrece, por ejemplo, un sistema integrado de tipo polimórfico Hindley-Milner. No se entrega con una extensa biblioteca estándar con soporte para programación GUI independiente de la plataforma y enlaces a otros idiomas. Tampoco se podría escribir una biblioteca de este tipo, ya que, entre otras cosas, Lazy K no proporciona ninguna forma de definir o hacer referencia a ninguna función que no sean las incorporadas. Esta incapacidad se complementa con una falta de compatibilidad con números, cadenas o cualquier otro tipo de datos. Sin embargo, Lazy K es Turing completo.

(...)

Los programas de Lazy K viven en el mismo reino eterno platónico que las funciones matemáticas, lo que la página de Unlambda llama "el reino bendito del cálculo lambda puro sin tipo". Así como la recolección de basura oculta el proceso de administración de memoria del programador, la transparencia referencial oculta el proceso de evaluación. El hecho de que sea necesario algún cálculo para ver una imagen del conjunto de Mandelbrot, o para "ejecutar" un programa Lazy K, es un detalle de implementación. Esa es la esencia de la programación funcional.

(...)

¿Cómo manejar la entrada y salida en un idioma sin efectos secundarios? En cierto sentido, la entrada y la salida no son efectos secundarios; son, por así decirlo, efectos frontales y posteriores. Así es en Lazy K, donde un programa simplemente se trata como una función desde el espacio de posibles entradas hasta el espacio de posibles salidas.

¡Dudo que encuentres un lenguaje más puramente funcional que ese!


Sin embargo, tenga en cuenta que lo anterior se aplica solo a esencialmente tomar la entrada y salida de una función pura y conectarlas a stdin / stdout "externamente" de alguna manera. Hay una gran diferencia entre eso y tener acceso a las primitivas de E / S reales de nivel de sistema. Los detalles de implementación de lectura y escritura en las secuencias pueden filtrar impurezas a menos que se encapsulen cuidadosamente.

Espero que esta sea la razón principal por la que no se puede hacer esto directamente en Haskell: los casos de uso razonables son escasos en comparación con el uso de IO monádico, y para este último hay un gran beneficio al tener acceso a la realidad. Creo que es por eso que, por ejemplo, los argumentos de la línea de comandos para el programa no se pasan simplemente como argumentos main, aunque parezca intuitivamente que deberían serlo.

Sin embargo, puede recuperar una versión mínima de algo como esto en un programa específico: simplemente capture los argumentos como valores puros y luego use la interactfunción para el resto de su programa.

CA McCann
fuente
Señor, debo confesar que disfruto su respuesta en cualquiera de las pilas. Definitivamente deberías escribir un libro de Haskell y NO estoy bromeando.
shabunc
@shabunc: A veces me he preguntado qué tan cerca está la suma total de mis respuestas en SO del tamaño de un libro ...
CA McCann
¿Podría dar un ejemplo de un sistema basado en una lógica lineal completa? Eso parece interesante, si existe.
configurador
@configurator: Observe cómo me vinculé a lenguajes específicos para los demás, pero ¿una página de wikipedia para lógica lineal? Por desgracia, si tuviera un ejemplo, lo habría dado. : [Todo lo que he oído son prototipos parciales y sistemas experimentales de la investigación de CS. Si desea profundizar en eso, aquí hay un material relativamente accesible en sistemas de tipo lineal que podría ayudarlo a comenzar.
CA McCann
3

Si bien la pureza en un programa funcional es un objetivo digno, la realidad es que cada programa útil no trivial tendrá cierta impureza (o "efectos secundarios"), por las razones que mencionó.

Los programas completamente puros son, por definición, una caja negra sellada, y son esencialmente poco interesantes.

El lenguaje funcional Haskell se ocupa de este problema aislando los efectos secundarios como la salida en mónadas . La mónada conserva un estilo de programación puramente funcional, al tiempo que permite producir resultados.

Robert Harvey
fuente
Claro, tienes razón. Pero entiendo por qué 100% de pureza es utopía. La pregunta es solo acerca de STDOUT.
shabunc
1
STDOUT es un efecto secundario, como cualquier otro. Internamente, la mónada realizaría cualquier verificación de error que pudiera ser necesaria.
Robert Harvey
Sí, de eso se trata esta pregunta: ¿por qué se considera un efecto secundario como cualquier otro?
shabunc
2
Cualquier cosa que modifique el mundo exterior se considera un efecto secundario.
Robert Harvey
1

Escribir en STDOUT puede fallar si no está conectado a un dispositivo terminal o si (por alguna razón) cerró el descriptor de archivo.

Además, STDOUT no siempre es "la pantalla de la consola". A veces se canaliza a otro programa. A veces la tubería está rota.

Ñame Marcovic
fuente
0

Ayuda si piensa en la pureza en términos de "Cambia el estado del mundo exterior". Eso podría incluir escribir en una consola, archivo de registro, expulsar el CD o "Lanzar los misiles".

También puede ser un problema en términos de ejecución concurrente. Si sabe que una función no tiene efectos secundarios, puede organizar fácilmente la concurrencia, ya que puede probar que no puede haber condiciones de carrera o similares.

Zachary K
fuente
Cambia el estado del mundo exterior o depende del estado del mundo exterior. Vea esta pregunta para más discusión en este sentido.
MatrixFrog