7

Escribí una función en haskell que toma algunos parámetros como Word32, String (ignorar currying) y salidas IO Word32. Ahora, esta es una función en el verdadero sentido: para las mismas entradas, la salida siempre será la misma. No hay efectos secundarios. La razón por la que la función devuelve IO Word32 en lugar de Word32 es que la función actualiza muchos registros de desplazamiento lineal de 32 bits (lfsr) y otros registros varias veces en un bucle para calcular la salida final de Word32.Cómo escribir una función haskell sin IO en tipo sig ocultando cambios de 'estado'

Mi pregunta es esta: Dado que esta función efectivamente no tiene efectos secundarios, ¿es posible ocultar esas actualizaciones de registro dentro de la implementación de la función para que la función devuelva Word32 y no IO Word32? ¿Si es así, cómo?

+0

¿Es importado por la FFI? – fuz

+1

¿Entiendo correctamente que el cuerpo de la función se escribe (parcialmente) en el estilo imperativo y se usa 'IORef's definido localmente para contener los LSFR? Tal vez deberías considerar usar 'STRef' en su lugar y ejecutar el cálculo dentro de una mónada' ST'. – rkhayrov

+1

¿Podría publicar el código para su función? Quizás debería estar en IO en primer lugar. – augustss

Respuesta

13

Sí! Haskell puede hacer esto.

El ST mónada

Si usted utiliza realmente el estado mutable (registros), que están ocultos por completo del observador fuera de la función, entonces usted está en the ST monad, una mónada sólo para efectos de memoria. Ingresa al mundo ST vía runST, y cuando sale de la función, se garantiza que todos los efectos no estarán visibles.

Es precisamente el entorno computacional adecuado para trabajar con un estado mutable local.

estado puramente funcional: la mónada Estado

Si, sin embargo, no estás realmente mutando registros o células, sino más bien la actualización de un valor puramente funcional en muchas ocasiones, un entorno más sencillo está disponible: the State monad. Esto no permite el estado mutable, pero da una ilusión de estado local.

IO, y unsafePerformIO

Por último, si usted tiene, efectos mutables locales, como en el ST mónada, pero por alguna razón u otra, se va a necesitar operaciones de IO en ese estado (por ejemplo, a través de una llamada FFI), puede simular la mónada ST, con casi tanta seguridad, al usar unsafePerformIO, en lugar de runST, para introducir un entorno IO local. Como la mónada IO no tiene buenos tipos para imponer la abstracción, deberá asegurarse manualmente de que los efectos secundarios no serán observables.

+0

Otro buen beneficio de la mónada ST es que es extremadamente eficiente. A menudo he encontrado que el código ST es más rápido que el código IO equivalente. –

+0

@ John L, de hecho, IO se define en términos de ST, con algunos ganchos extra mágicos. –

+0

Don, el estado mutable está completamente oculto de la función externa del observador. Leí un artículo de 1994 llamado Lazy Functional State Threads por John Launchbury y Simon Peyton, que dice * Algunos algoritmos hacen un uso interno crítico del estado actualizable, aunque su especificación externa es puramente funcional ... presentamos una forma de encapsular computaciones con estado que manipular objetos múltiples, nombrados, mutables, en el contexto de un lenguaje no estricto, puramente funcional *. Papel, que habla de ST, fue un poco demasiado técnico la primera vez que lo leí. Lo leeré nuevamente e implementaré f8, f9 usando la mónada ST. –

5

Si importó esa función utilizando el FFI, simplemente elimine el IO del tipo de devolución. De lo contrario, use unsafePerformIO :: IO a -> a de System.IO.Unsafe. Tenga en cuenta que esta función es una de las más peligrosas en Haskell. No lo use, si no está realmente seguro sobre las consecuencias. Pero para su propósito, parece estar bien.

+6

Probablemente tenga en cuenta que uno de los pocos usos "correctos" de 'unsafePerformIO' es como una afirmación plana para el compilador de que una función en realidad es externamente pura, que es exactamente de lo que se trata esta pregunta. La mayoría de las formas en que 'unsafePerformIO' puede morderlo surgen de su uso en funciones que son solo "en su mayoría" puras en algún sentido. –

+0

Quizás el mal uso más molesto de 'unsafePerformIO' sea en presencia de almacenamientos intermedios estáticos (no es un uso seguro de subprocesos), ¡así que tenga mucho cuidado! –

+2

@TomMD: A veces pienso que es contraproducente hablar de pureza en términos de "efectos secundarios" que implican "hacer algo" tanto como los programadores de Haskell tienden a hacerlo. Los errores más insidiosos en las funciones impuras, y en la mayoría de los lenguajes impuros, a menudo son aquellos que implican acceso de solo lectura al mundo exterior. –

3

Sí, este sería un uso legítimo de inseguroPerformIO. Pero solo está bien si está realmente seguro de que no hay efectos visibles.