5

Consider the following code (with obvious parts left out)

main = do

    let s = "123456";
    let len = runReader calculateContentLength s
    putStrLn $ "Original 's' length: " ++ (show len)


calculateContentLength :: Reader String Int
calculateContentLength = do
    content <- ask           -- this seems to be the same as 'reader id'
    return (length content);

How does 'ask' get at the string parameter? It's my understanding that because of the type declaration

calculateContentLength :: Reader String Int

the function 'calculateContentLength' has a return type (of type Reader String Int) but it has no incoming arguments. I realize that the function itself is simply one of two arguments being passed into the runReader function but how exactly does the second parameter to runReader, 's', get tied to 'ask' inside 'calculateContentLength'?

In other words, how does 'calculateContentLength' "know" about (and get access to) the second argument passed with 'runReader'?

David
  • 5,991
  • 5
  • 33
  • 39
  • By the way, I'm certain currying is involved but I'm just not seeing the details properly. – David Aug 24 '14 at 20:52
  • 3
    I encourage you to use the source for `runReader` and `ask` to prove the simple equation `runReader ask s = s`. Then see if you can use the proof as intuition fuel. – Daniel Wagner Aug 24 '14 at 22:09

1 Answers1

13

Let’s look at one way to define Reader.

newtype Reader r a = Reader { runReader :: r -> a }

So Reader is a constructor that takes a function. That function takes the environment of type r, and returns a result of type a.

ask = Reader { runReader = \env -> env }
ask = Reader id

The return operation just ignores the environment and returns a value.

return x = Reader { runReader = \_ -> x }

The m >>= n operation does simple sequencing: it takes the environment, runs m in that environment, then runs n in the same environment, passing it the result of m.

m >>= n = Reader $ \env -> let
  a = runReader m env
  in runReader (n a) env

So now we can take your example, desugar it, and reduce it step by step.

calculateContentLength = do
  content <- ask
  return (length content)

-- substitute definition of 'ask'

calculateContentLength = do
  content <- Reader id
  return (length content)

-- substitute definition of 'return'

calculateContentLength = do
  content <- Reader id
  Reader (\_ -> length content)

-- desugar 'do' into '>>='

calculateContentLength =
  Reader id >>= \content -> Reader (\_ -> length content)

-- definition of '>>='

calculateContentLength = Reader $ \env -> let
  a = runReader (Reader id) env
  in runReader ((\content -> Reader (\_ -> length content)) a) env

-- reduce lambda

calculateContentLength = Reader $ \env -> let
  a = runReader (Reader id) env
  in runReader (Reader (\_ -> length a)) env

-- definition of 'runReader'

calculateContentLength = Reader $ \env -> let
  a = id env
  in runReader (Reader (\_ -> length a)) env

-- definition of 'id'

calculateContentLength = Reader $ \env -> let
  a = env
  in runReader (Reader (\_ -> length a)) env

-- remove redundant variable

calculateContentLength = Reader $ \env
  -> runReader (Reader (\_ -> length env)) env

-- definition of 'runReader'

calculateContentLength = Reader $ \env -> (\_ -> length env) env

-- reduce

calculateContentLength = Reader $ \env -> (length env)
calculateContentLength = Reader length

Now it should be easier to see how runReader calculateContentLength is the same as just length, and how ask is not magical—the monad’s >>= operation builds a function that implicitly passes the environment along for you when you run the computation with runReader.

In reality, Reader is defined in terms of ReaderT, which uses monadic actions instead of pure functions, but the form of its implementation is essentially the same.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • Thank you --- what I completely forgot about was the "do" at the beginning of the function. So I was missing the entire binding part. That's why I couldn't see what was going on. Thank you for the detailed explanation. – David Aug 25 '14 at 03:29