简体   繁体   中英

How to use function instance of reader monad?

I have found an interesting use of this monad:

Prelude Control.Monad> liftM2 (,) head tail $ [1..5]
(1,[2,3,4,5])

It looks like useful technique that allows r in (->) r to be passed only once, where I would expect this expression to require duplicating the list first.

I don't quite understand how lifting actually works here. Where is >>= or return hidden? What are other common situations which would call for using this particular instance?

liftM2 takes a binary function and two monadic values and applies the function over the values inside the monad. Look at the implementation:

liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = do 
  x1 <- m1
  x2 <- m2
  return (f x1 x2)

Or if we desugar it we can see the explicit (>>=) and return :

liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = 
  m1 >>= (\x1 ->
  m2 >>= (\x2 ->
  return (f x1 x2)))

This is useful whenever you have a need to apply a pure function inside a monad, the use cases are pretty wide since it's a very common task.

(Read freyrs's answer first; this answer expands upon it.)

See the defintion of the reader/Function monad instance

instance Monad ((->) r) where
  return = const
  f >>= k = \ r -> k (f r) r

You can see there where the input gets duplicated/forked ( r appears twice on the right-hand-side of the \\ r expression): Each value that is passed to (>>=) (both head and tail , in your example) is then passed the same argument ( [1,..5] ) when the combined (monadic) function is applied to that argument.

Without using monadic liftM2 , the (function application) expression (,) head tail just creates a tuple (head, tail) . But when (as in liftM2 ) monadic bind is applied instead of "plain" function application, those arguments are bound ("bind-ed") into the monadic value, where they remain "ready" to receive the bind result's function argument.

Note also that the final argument ( r = [1..5] ) is used once for each call to (>>=) (which happens twice in liftM2 , that's what the 2 means). The return causes no additional use of the final argument, since in this monad we have return fr = const fr ( fr added for explicitness), which ignores the argument r .

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