2012-06-12 26 views
5

Tengo un módulo donde se crea e inicializa un entorno global (que define ciertas restricciones como direcciones IP contiguas, etc.) llamando a una función de inicialización. Varias funciones posteriores deben usar estas restricciones cuando se invocan.Variables globales y la mónada del lector

Si bien, en principio, entiendo lo que hace la mónada del lector, no estoy muy seguro de cómo puedo aplicar esto a mi problema, esp.

  • cómo se puede utilizar para inicializar un entorno que está definido por el usuario y pasa como datos/argumentos de la función de inicialización. Quiero decir, la mónada del lector tiene que obtener los valores reales que componen el entorno global inmutable de algún lado. Me gustaría que los valores se leen de una llamada a la función de inicialización como myinitial :: arg1 -> arg1 -> IOString donde posteriormente arg1 y arg2 vuelto global de datos inmutables accesibles a las funciones posteriores a través de la mónada lector (?)

  • ¿Cómo puedo utilizar estos valores de entorno como argumentos de funciones p.ej recvFrom s arg1 donde arg1 son datos globales inmutables de mi entorno. O if arg2 > arg1 then ... else ...

que podría hacer un archivo de configuración, por supuesto, pero creo que un archivo de configuración quitará a mucha flexibilidad.

[Editar] Entiendo acerca de preguntar, pero ¿no debería haber formas adicionales "parecidas a las de un punto" para que el global/entorno inmutable pueda omitirse si la firma de la función se ha definido, ¿verdad? ¿Cómo podría, por ejemplo, tener que refactorizar mi if-then-else para aplicar this?

Respuesta

4

Aquí hay un ejemplo que puede aclarar las cosas.En primer lugar es necesario importar el módulo lector:

import Control.Monad.Reader 

Ahora vamos a definir una estructura de datos (que vamos a utilizar para mantener un nombre y una edad)

data Config = Config { name :: String, age :: Int } 

Ahora definir una función que funciona en la mónada Reader (su tipo es Reader Config (String, Int) pero no es necesario que lo especifiquemos, se puede inferir). Todo lo que esta función hace es preguntar por el entorno (del tipo Config) y luego extrae los campos y hace algo con ellos.

example = do 
    c <- ask 
    return ("Hello " ++ name c, 2 * age c) 

Ahora lo ponemos todo junto en un programa. Las primeras cuatro líneas después del bloque do le permiten al usuario ingresar su nombre y edad. Entonces podemos construir una estructura Config utilizando las entradas del usuario (que tenemos que utilizar read para convertir la variable _age, que es un String, en un Int para que podamos alimentar a la Config constructor) y ejecutar example con este entorno, utilizando el runReader función. Finalmente, usamos el resultado de este cálculo para generar algún resultado.

main = do 
    putStrLn "Enter your name:" 
    _name <- getLine 
    putStrLn "Enter your age:" 
    _age <- getLine 
    let config = Config _name (read _age) 
    let result = runReader example config 
    putStrLn $ fst result 
    putStrLn $ "Twice your age is: " ++ show (snd result) 
+0

Funciona parcialmente, como se menciona en la pregunta, tengo un ** módulo ** que tiene varias funciones; entre ellos hay una función de inicialización que crea el ambiente. Las funciones del módulo que se llaman más tarde deben usar el entorno. ¿Es eso posible con la mónada del lector? –

+0

Pero, ¿cómo debería funcionar esto? En su/mi ejemplo, la función de inicializador diría algo como 'let config = Config blabla' y una función posterior tendría que hacer' let result = runReader example config'; - Sin embargo, la función siguiente no conoce 'config'. –

+0

Mira [esta esencia] (https://gist.github.com/2924160) que contiene un ejemplo simple. En el módulo 1, definimos una función que inicializa un entorno global y algunas funciones que dependen del entorno global. El Módulo 2 solo contiene una función principal que inicializa el entorno, y llama a las funciones que dependen de él utilizando 'runReader' (** Edit: ** disculpas, eliminé el comentario original que contestó y lo reemplacé con este). –

5

La mayoría de sus preguntas pueden responderse inspeccionando los tipos y la documentación de las funciones ask y runReader.

primer lugar, el ask:

ask :: Reader m r => m r 

Esto devuelve el de sólo lectura de datos subyacentes envueltos en la mónada. Fresco, así que eso es cómo va a llegar al estado cuando desea utilizarlo con otras funciones, en su ejemplo anterior:

do x <- ask 
    recvFrom s x 

(dependiendo del tipo de recvFrom, por supuesto)

siguiente es el runReader, así es como le das los datos iniciales de los que hablabas. Es, básicamente, sólo se ejecuta el cálculo Reader utilizando los datos que se le da:

runReader :: Reader r a -> r -> a 

que significa: ejecutar el cálculo (el primer argumento) con los datos de sólo lectura de tipo r (el segundo argumento). Finalmente devolverá el tipo de resultado del primer argumento, a. En su caso, esto puede verse como:

result = runReader computationUsingArg1Arg2 (arg1, arg2) 

A continuación, en el interior computationUsingArg1Arg2 se puede leer a través de arg1 y arg2ask.

+0

Gracias, todavía no entiendo cómo aplicar el 'runReader' a mi viñeta de ejemplo, una de mi pregunta. –

+0

He ampliado la respuesta un poco, avíseme si todavía hay algo que no está claro. – ScottWest

+0

Sé que es un año más tarde, pero para mí esto no responde la pregunta. El problema no es solo que haya compuationUsingArg1Arg2, sino que computationUsingArg1Arg2 pueda querer llamar OTRAS funciones que también necesitan acceso a la información "global". De lo contrario, podría pasar los parámetros directamente y no necesitar el lector. Entonces, ¿cómo se definen e invocan todas esas otras funciones? – David