I am new at haskell, I have to write a program context-aware,so I thought I can use the Reader Monad for keeping the context read from a file, I know how to read the file puting the content in a list of tuplessomething like [([Char],[Char])], but I do not know how to implement the Reader Monad for making the environment available to all the components of my program without using imperative style, In particular I do not know how to set and use the environment, as far as I understood I should give it as parameter to all the functions that need the environment with runReader function env, but I am very confused, can somebody give me some indications or a good tutorial? thanks in advance
The Reader monad (also called the Environment monad). Represents a computation, which can read values from a shared environment, pass values from function to function, and execute sub-computations in a modified environment. Using Reader monad for such computations is often clearer and easier than using the State monad.
A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.
The state monad is a built in monad in Haskell that allows for chaining of a state variable (which may be arbitrarily complex) through a series of function calls, to simulate stateful code.
The basic scheme for using any "normal" monad[0] is pretty much the same across the board. Essentially:
do
notation if you like, just like you'd write an IO
function like main
.<-
to get at the value "inside", causing the other value to be "run".let
, leaving it independent of the monadic structure.Do that, and all the messy details of the extra functionality described by the monad (in this case, passing an extra environment parameter around) are handled automatically.
Now, the usual Reader operations are ask
and local
:
ask
is a monadic value holding the environment; in a do
block you use it the same way you'd use something like getLine
in the IO
monad.local
takes a function that provides a new environment and a computation in the Reader monad, runs the latter in an environment modified by the former, then takes the result and puts it into the current function. In other words, it runs a sub-computation with a locally modified environemnt.The "run" function is the creatively-named runReader
, which simply takes a computation in the Reader monad and an environment value, runs the former using the latter, and returns the final result outside of the monad.
As an example, here's some functions doing some meaningless calculation in a Reader monad, where the environment is a "maximum value" that says when to stop:
import Control.Monad.Reader
computeUpToMax :: (Int -> Int) -> Int -> Reader Int [Maybe Int]
computeUpToMax f x = do
maxVal <- ask
let y = f x
if y > maxVal
then return []
else do zs <- local (subtract y) (computeUpToMax f y)
z <- frob y
return (z:zs)
frob :: Int -> Reader Int (Maybe Int)
frob y = do
maxVal <- ask
let z = maxVal - y
if z == y
then return Nothing
else return $ Just z
To run it, you'd use something like this:
> runReader (computeUpToMax (+ 1) 0) 9
[Just 8, Just 6, Nothing]
...where 9
is the initial environment.
Almost exactly the same structure can be used with other monads, such as State
, Maybe
, or []
, though in the latter two cases you'd typically just use the final monadic result value instead of using a "run" function.
[0]: Where normal means not involving compiler magic, the most obvious "abnormal" monad of course being IO
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With