How to use the Reader Monad with (Int -> Int)?

292 views Asked by At

I would like to learn, how to use the Reader Monad. Unfortunately only a small amount of example code is available

I would like to create a Reader, where the environment and the retrived values are Int. I defined the type like this:

type IntRead = Reader Int Int

The code I tried is this:

addStuff :: Reader Int Int
addStuff = do  
    a <- (*2)  
    b <- (+10)
    return (a+b)  

I get an error, because ReaderT is expected. How to create a function like addStuff using the Reader Monad? Where should I provide the environment to this function?

1

There are 1 answers

6
chi On BEST ANSWER

You can convert functions to readers and back with these two isomorphisms:

reader :: (r -> a) -> Reader r a
runReader :: Reader r a -> r -> a

E.g.

addStuff :: Reader Int Int
addStuff = do  
    a <- reader (*2)  
    b <- reader (+10)
    return (a+b)

and then you can test your code with runReader addStuff 5.

This is OK for learning purposes. For more serious code, you shouldn't use the isomorphisms that much, but instead rely on ask or asks. E.g.

addStuff :: Reader Int Int
addStuff = do  
    x <- ask   -- fetch the implicit Int value
    let a = (*2) x
        b = (+10) x
    return (a+b)

or, better

addStuff :: Reader Int Int
addStuff = do  
    a <- asks (*2) -- fetch the implicit Int value, and apply the function
    b <- asks (+10)
    return (a+b)

or, even better, using applicative style:

addStuff :: Reader Int Int
addStuff = (+) <$> asks (*2) <*> asks (+10)

The whole point of the reader abstraction is not to think about the underlying function. You can just pretend to have access to a read-only variable, which is accessible through the ask primitive.

Usually, only at the very last step you use runReader to actually use your monadic reader action.