简体   繁体   中英

How does the Reader monad's "ask" function work?

data T b = E | N b (T b) (T b) 


f :: T b -> Reader Int (T Int)
f (N i l r) = ask >>= \x ->  local ((-)4) (f l) >>= \l' -> local ((-)1) (f r) >>= \r' -> return (N x l' r')
f E = return E

I have a problem with understanding how this code works. Especially, how does ask know where the environment is (in our case just Int )?

To be more precise: I am an imperative programmer and in such languages it is easy. Methods can be called on any object like: obj.f() , or we have to pass data by argument when we want function use external data.

That's kind of what the Reader monad does ; it gives you an ask function, which "magically" pops up a value out of thin air. To actually use this, you need to call runReader , and give it the Int environment. The Reader type then automatically propagates that from the runReader call to each of the ask calls.

Short, hand-wavy answer. The Reader Int (T Int) value is essentially just a wrapped-up function of type Int -> (T Int) . In order to run that function, we first need to extract it. We do that with runReader .

data T b = ... deriving (Show)

main = let tree = (N 10 (N 8 E E) E)
           g = f tree
           h = runReader g
       in print $ h 20

(Typically, you would simply write print $ runReader (f tree) 20 ; I split it up to correspond to the sketchy analogy below.)

ask (defined by the MonadReader typeclass and as implemented by the ReaderT monad transformer used to define the Reader type) essentially retrieves the value of the argument passed to h .

In some sense, the Reader Int (T Int) is an object that contains a function g that calls a function ask . runReader g creates a new function which, when called, defines a function ask that simply returns its argument, then calls g . Now when g calls ask , it gets back the argument originally passed to h .

I recomend reading this first. Ask is defined:

ask :: (Monad m) => ReaderT r m r
ask = ReaderT return

and reader:

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
type Reader r = ReaderT r Identity

so

do
  r <- ask
  ...

is equivalent to

do
  r <- ReaderT return
  ...

So essentially <- just reaches into the identity monad, and grabs what ever value that will eventually be lifted by runReader R = return .

This enables global variables in haskell.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM