How to interpret bind/>>= of the function instance?

拟墨画扇 提交于 2019-11-28 01:56:40

The values in the monad instance for functions have type r -> a for some fixed type r. The function (a -> (r -> b)) given to (>>=) allows you to choose the next function to return given the result from the current value (a function r -> a). f r has type a and k (f r) has type r -> b which is the next function to apply.

In your code g(f(x)) is therefore a function which expects a single argument of type r. The caller of bind can choose this function based on the value returned by the previous function e.g.

var inc = x => x + 1;
var f = bind(inc)(function(i) {
   if(i <= 5) { return x => x * 2; }
   else { return x => x * 3; }
});

The function will be given x as an input and can choose the next stage in the computation based on the result of inc(x) e.g.

f(2) //4;
f(5) //15;

A few footnotes to Lee's answer:

However, the bind function looks pretty weird. Why are f and g nested the other way around?

Because bind is backwards. Compare (>>=) and its flipped version (=<<):

(>>=) :: Monad m => m a -> (a -> m b) -> m b
(=<<) :: Monad m => (a -> m b) -> m a -> m b

Or, in your specific example:

(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
(=<<) :: (a -> (r -> b)) -> (r -> a) -> (r -> b)

While in practice we tend to use (>>=) more often than (=<<) (because of how (>>=), syntactically speaking, lends itself well to the kind of pipeline monads are often used to build), from a theoretical point of view (=<<) is the most natural way of writing it. In particular, the parallels and differences with fmap/(<$>) and (<*>) are much more obvious:

(<$>) :: Functor f     =>   (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(=<<) :: Monad f       => (a -> f b) -> f a -> f b

When I apply apply/bind with an unary and a binary function, they yield the same result. This doesn't make much sense.

That is an accidental fact about the function instances. Let's put the specialised signatures side by side:

(<*>) :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
(=<<) :: (a -> (r -> b)) -> (r -> a) -> (r -> b)

Monad goes beyond Applicative by providing the means to determine the next effect according to previous results (as opposed to "previous effect" -- Applicative can do that already). The effect, in this case, consists of a function that generates values given an argument of type r. Now, since functions with multiple arguments (i.e. functions that return functions) can be flipped, it happens that there is no significant difference between (r -> (a -> b)) and (a -> (r -> b)) (flip can trivially change one into the other), which makes the Monad instance for (->) r entirely equivalent to the Applicative one.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!