I'm mostly a practical guy but I find this interesting.
I have been thinking about monadic sequencing and there are a few things that I need clarified. So at the risk of sounding silly here it is:
The monadic member bind
bind :: m b -> (b -> m c) -> m c
can sequence "actions" giving you explicit access to intermediate values.
How does this give me more than the categorical member (.):
(.) :: cat b c -> cat a b -> cat a c
With this I can sequence and get access to intermediate values.
After all (f . g) x = f(g (x)).
Why do I need bind for sequencing if I can sequence with (.)?
You're on the right track. Every monad gives rise to so-called Kleisli category. For every monad m its corresponding Kleisli category has arrows a -> m b and they can be composed using >=>, which is defined as
f >=> g = \x -> f x >>= g
Kleisli type encapsulates this in Haskell type system, you can see that it has instance
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (g >=> f)
So sequencing computations within this category is just sequencing operations using >=>, which can be expressed equivalently using >>=.
We define monads using return and >>= because it's more convenient, but we could define them as well using return and >=> if we wanted.
(See also my answer to Different ways to see a monad.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With