Skip to content

What is the purpose of the reader monad?

This blog answered the question of What is the purpose of the reader monad? in my view. The original answer stays on stackoverflow.

I have learned much from the above answers, and thanks them for answering. Although I'm new to Haskell, I want to say something about the Reader monad.

First, treating a Reader as a Computation is important. It's not a state, but a computation. For example, the function calc_isCountCorrect mentioned in first official example of Reader monad returns a Reader Bindings Bool, which means it receives a Bindings when runReader, and return a Bool. It's a computation.

calc_isCountCorrect :: Reader Bindings Bool
calc_isCountCorrect = do
    count <- asks (lookupVar "count")
    bindings <- ask
    return (count == (Map.size bindings))

You can also pass the Bindings through an argument, there is no significant differences when your code is quite simple.

calcIsCountCorrectWithoutReader :: Bindings -> Bool
calcIsCountCorrectWithoutReader bindings = do
  let num = lookupVar "count" bindings
  let count = Map.size bindings
  num == count

However, it does have a difference, that's where the value comes from. It gives you a way to retrieve it by an implicit source instead of an argument.

When it comes to the question of analogy, I think the lambda in C++ is a good explanation.

In an imperative language like Java or C++, there is no equivalent concept for the reader monad.

The reader gives you abilities to retrieve a value from outside(NOT global variable, but an upper scope). It's pretty like a capture clause in C++ lambda.

For example, you have a haskell code that trebles number and adds it with an implicit value. The following code outputs 10.

import           Control.Monad.Reader
trebleNumberAndAddImplicitly :: Int -> Reader Int Int
trebleNumberAndAddImplicitly number = do
  implicitNumber <- ask
  return $ 3*number + implicitNumber

main :: IO ()
main = do
  print $ runReader (trebleNumberAndAddImplicitly 3) 1

The implicit value is outside the COMPUTATION but accessible with Reader.

Inside C++, this is called capture clause. The output is: output is: 10. However, it has more limitations than haskell. But it's similar in my mind.

#include<iostream>

int main(){
    int implicitNumber = 1;
    auto trebleNumberAndAddImplicitly = [implicitNumber](int number) -> int {
        return (3*number + implicitNumber);
    };
    std::cout << "output is: " << trebleNumberAndAddImplicitly(3);
}