With monads, can join be defined in terms of bind?

In Haskell, monads are defined in terms of the functions return and bind, where return has type a -> ma and bind has type ma -> (a -> mb) -> mb . It's been pointed out before that monads can also be defined in terms of return and join, where join is a function with type m (ma) -> ma . Bind can be defined in terms of join, but is the reverse possible? Can join be defined in terms of bind?

Without join, I have no idea what I'd do if I ever somehow got ahold of a "twice wrapped" monadic value, m (ma) - none of the functor or monad operations "remove any layers", so to speak. If this isn't possible, why do Haskell and many other monad implementations define them in terms of bind? It seems strictly less useful than a join-based definition.


It is possible:

join :: Monad m => m (m a) -> m a
join m = (m >>= id)

Note the tricky instantiation of >>= :

(>>=) :: m b -> (b -> m c) -> m c
-- choosing b ~ m a , c ~ a
(>>=) :: m (m a) -> (m a -> m a) -> m a

so we can correctly choose id for the second argument.


是的,这很简单:

join m = m >>= id

Bind ( >>= ) does in fact "remove a layer":

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

Intuitively it "gets some a s out of the ma ", and feeds then to the a -> mb function, and then produces a single mb from the results.

People usually say that it requires the function argument to wrap up its output again in m , but that's not actually the case. It requires the function's output to be something wrapped up in m , but it doesn't matter where the wrapping came from.

In the case of implementing join we're starting from something "double-wrapped": m (ma) . We can plug that into the signature for bind and immediately figure out the type of function we could use when binding a "double-wrapped" value:

m (m a) -> (m a -> m b) -> m b

Now the function used with bind here is going to receive a value that's already wrapped in m . So we don't have to "re-wrap" anything; if we return it unmodified it'll already be the right type for the output. Effectively that's "removed one layer of wrapping" - and this works for any layer but the last one.

So that tells us we just have to bind with id :

join = (>>= id)
链接地址: http://www.djcxy.com/p/33224.html

上一篇: 如何升级堆栈ghc

下一篇: 对于monad,可以通过bind来定义join吗?