43

Haskell generalmente se menciona como un ejemplo de un lenguaje puramente funcional. ¿Cómo puede justificarse esto dada la existencia de System.IO.Unsafe.unsafePerformIO?¿Haskell es realmente un lenguaje puramente funcional considerando UnpafePerformIO?

Editar: Pensé que con "puramente funcional" se entiende que es imposible introducir código impuro en la parte funcional del programa.

+0

Con respecto a su edición: Sin embargo, todavía es posible crear una biblioteca funcional que se puede llamar desde un programa imprescindible. En última instancia, le corresponde al programador decidir cuánta funcionalidad está presente. –

+1

No es imposible introducir código impuro, simplemente no es el predeterminado. Y el lenguaje llega a extremos significativos para desalentarlo. Sin embargo, el código impuro ocasionalmente es útil. Especialmente cuando está escondido de forma segura detrás de una interfaz pura. –

+5

Vea también http://stackoverflow.com/questions/1916692/are-side-effects-possible-in-pure-functional-programming y http://stackoverflow.com/questions/1916692/are-side-effects-possible -in-pure-functional-programming y http://stackoverflow.com/questions/2488646/why-are-side-effects-modeled-as-monads-in-haskell –

Respuesta

114

Las lenguas que llamamos Haskell

unsafePerformIO es parte de la función de Exteriores especificación de interfaz, no Haskell 98 núcleo especificación. Se puede usar para hacer efectos secundarios locales que no escapan a algún ámbito, a fin de exponer una interfaz puramente funcional. Es decir, lo usamos para ocultar los efectos cuando el verificador de tipos no puede hacerlo por nosotros (a diferencia de la mónada ST, que oculta los efectos con una garantía estática).

Para ilustrar con precisión los múltiples idiomas que llamamos "Haskell", considere la siguiente imagen. Cada anillo corresponde a un conjunto específico de características computacionales, ordenadas por seguridad, y con un área que se correlaciona con el poder expresivo (es decir, la cantidad de programas que puede escribir si tiene esa característica).

El lenguaje conocido como Haskell 98 se especifica justo en el medio, admitiendo funciones totales y parciales. Agda (o Epigram), donde solo se permiten las funciones totales, es aún menos expresivo, pero "más puro" y más seguro. Mientras que Haskell, tal como lo usamos hoy, incluye todo en FFI, donde vive InseguroPerformIO. Es decir, puede escribir cualquier cosa en Haskell moderno, aunque si utiliza elementos de los anillos exteriores, será más difícil establecer garantías de seguridad simplificadas por los anillos interiores.

alt text

Por lo tanto, los programas de Haskell no suelen ser construido a partir de 100% de código referencialmente transparente, sin embargo, es el único lenguaje moderadamente común que es puro por defecto.

+0

¿Te entiendo correctamente, que la gente debería decir más precisamente "Haskell 98 es un lenguaje funcional puro" y no "Haskell es un lenguaje puramente funcional"? –

+22

@Stefan Schmidt: más parecido a "Haskell * es * puramente funcional", pero "GHC es una implementación de un superconjunto de Haskell que incluye la Interfaz de Función Extranjera que no es puramente funcional" – yairchu

+1

¡Gracias por el interesante gráfico! –

4

El idioma/implementación es puramente funcional. Incluye un par de "escotillas de escape", que no tienes que usar si no quieres.

1

Lamentablemente, el lenguaje tiene que hacer un poco de trabajo en el mundo real, y esto implica hablar con el entorno externo.

Lo bueno es que puede (y debe) limitar el uso de este código "desactualizado" a algunas partes específicas bien documentadas de su programa.

+1

¿Desafortunadamente? Diría "afortunadamente" :) Ya no estamos "evitando el éxito a toda costa". – Artyom

34

pensé con "puramente funcional" se entiende que es imposible introducir el código impura ...

La verdadera respuesta es que es unsafePerformIOno parte de Haskell, como tampoco lo digamos, el recolector de basura o el sistema de tiempo de ejecución son parte de Haskell. unsafePerformIO está allí en el sistema para que las personas que crean el sistema puedan crear una abstracción funcional pura sobre hardware muy efectivo. Todos los lenguajes reales tienen lagunas que hacen posible que los creadores de sistemas hagan las cosas de manera más efectiva que bajar al código C o al código ensamblador.

En cuanto a la imagen más amplia de cómo los efectos secundarios y de E/S encajaba en Haskell a través de la IO mónada, creo que la forma más fácil de pensar en Haskell es que es un lenguaje puro que describe cálculos effectful. Cuando el cálculo descrito es main, el sistema en tiempo de ejecución lleva a cabo esos efectos fielmente.

unsafePerformIO es una forma de obtener efectos de una manera insegura; donde "inseguro" significa "la seguridad debe ser garantizada por el programador" — nada es verificado por el compilador. Si usted es un programador experto y está dispuesto a cumplir con las obligaciones de prueba, puede usar unsafePerformIO. Pero en ese momento ya no estás programando en Haskell; estás programando en un lenguaje inseguro que se parece mucho a Haskell.

+0

http://www.haskell.org/ghc/docs/6.12.2/html/libraries/ ¿Qué es "parte de Haskell" en su comprensión? –

+3

@StefanSchmidt: algo que se ha definido en el estándar de lenguaje. Solo porque algo sea parte de ghc, no es parte de haskell más que cualquier cosa en gcc es parte del lenguaje C. – sepp2k

+9

@Stefan: para preguntas como esta, "Haskell" significa el estándar * Haskell 2010 *, o posiblemente el estándar * Haskell 98 * publicado por Cambridge University Press en 2003. En otros contextos, muchos programadores usan la palabra "Haskell" para describe lo que sea que GHC implemente esa semana. –

0

Tengo la sensación de que seré muy impopular por decir lo que voy a decir, pero sentí que tenía que responder a parte de la información (en mi opinión errónea) presentada aquí.

Si bien es cierto que insafePerformIO se agregó oficialmente al idioma como parte del anexo de FFI, las razones de esto son en gran parte históricas en lugar de lógicas. Existía extraoficialmente y fue ampliamente utilizado mucho antes de que Haskell tuviera alguna vez un FFI. Nunca fue oficialmente parte del estándar principal de Haskell porque, como habrás observado, fue demasiado vergonzoso. Creo que la esperanza era que desaparecería en algún momento en el futuro, de alguna manera. Bueno, eso no ha sucedido, ni lo hará en mi opinión.

El desarrollo de FFI Addendum proporcionó un pretexto conveniente para inseguroPerformIO para obtener snuck en el estándar de idioma oficial ya que probablemente no parece tan malo aquí, en comparación con agregar la capacidad de llamar al código extranjero (IE C) (donde todas las apuestas están apagadas con respecto a asegurar estáticamente la pureza y la seguridad de tipo de todos modos). También fue conveniente ponerlo aquí por razones esencialmente políticas. Fomentó el mito de que Haskell sería puro, si no fuera por todo ese sucio "mal diseñado" C, o sistemas operativos "mal diseñados", o hardware "mal diseñado" o lo que sea ... Es cierto que inseguroPerformIO se usa regularmente con código relacionado con FFI, pero las razones para esto a menudo tienen más que ver con el mal diseño de la FFI y, en realidad, con Haskell, no es un mal diseño de cualquier cosa extraña que Haskell intente también con la interfaz.

Así que, como dice Norman Ramsey, la posición oficial fue que estaba bien usar unPerformIO inseguro siempre que ciertas obligaciones de prueba fueran satisfechas por quienquiera que lo usara (principalmente que esto no invalida las transformaciones importantes del compilador como -presión de expresión). Hasta aquí todo bien, o al menos uno podría pensar. El verdadero truco es que estas obligaciones de prueba no se pueden satisfacer por lo que es probablemente el caso de uso más común para inseguroPerformIO, que según mi estimación representa más del 50% de todas las formas de riesgo inseguras que hay en la naturaleza. Estoy hablando de la espantosa expresión idiomática conocida como "piratería insegura de PerformIO", que es demostrable (de hecho obviamente) completamente insegura (en presencia de inline y cse).

Realmente no tengo el tiempo, el espacio o la inclinación para entrar en lo que es el "truco inseguro dePerformIO" o por qué es necesario en bibliotecas IO reales, pero la conclusión es que la gente que trabaja en la infraestructura IO de Haskells "atrapado entre una roca y un lugar duro". Pueden proporcionar una API intrínsecamente segura que no tiene una implementación segura (en Haskell), o pueden proporcionar una API intrínsecamente insegura que se puede implementar de forma segura, pero lo que rara vez pueden hacer es proporcionar seguridad en la implementación del diseño de API y.A juzgar por la deprimente regularidad con la que aparece el "truco inseguro de PerformIO" en el código del mundo real (incluidas las bibliotecas estándar de Haskell), parece que la mayoría elige la opción anterior como el menor de los dos males, y solo espera que el compilador no las cosas con inlining, cse o cualquier otra transformación.

Ojalá todo esto no fuera así. Desafortunadamente es.

+3

Quería votarte por la respuesta, pero creo que ver qué es lo que el "truco no seguro de PerformIO" es o por qué se necesita en las bibliotecas IO reales es lo que hace que tu respuesta sea útil. La mayor parte de tu respuesta es solo manual. –

+0

Supongo que el "truco inseguro de PerformIO" es el mismo que se describe en http://www.haskell.org/haskellwiki/Top_level_mutable_state – hvr

2

No creo que inseguroPerformIO signifique que haskell de alguna manera se vuelve impuro. Puede crear funciones puras (referencialmente transparentes) a partir de funciones impuras.

Considere la lista de skiplist. Para que funcione bien, requiere acceso a un RNG, una función impura, pero esto no hace que la estructura de datos sea impura. Si agrega un elemento y luego lo convierte en una lista, se le devolverá la misma lista cada vez que le proporcione el artículo que agrega.

Por esta razón, creo que unaPerformIO insegura debe considerarse como una promesaPureIO. Una función que significa que las funciones que tienen efectos secundarios y que, por lo tanto, podrían etiquetarse impuras por el sistema de tipos pueden ser reconocidas como referencialmente transparentes por el sistema de tipos.

Entiendo que para que esto suceda es necesario tener una definición un poco más débil de pure. Es decir, las funciones puras son referencialmente transparentes y nunca se llama porque tiene un efecto secundario (como imprimir).

1

Safe Haskell, una extensión reciente de GHC, ofrece una nueva respuesta a esta pregunta. unsafePerformIO es una parte de GHC Haskell, pero no es parte del dialecto seguro.

unsafePerformIO debe utilizarse solo para crear funciones transparentes de referencia; por ejemplo, memorización. En estos casos, el autor de un paquete lo marca como "confiable". Un módulo seguro solo puede importar módulos seguros y confiables; no puede importar módulos inseguros.

Para más información: GHC manual, Safe Haskell paper

Cuestiones relacionadas