Is monad bind (>>=) operator closer to function composition (chaining) or function application?

Clearly, >>= is not a way to represent function composition. Function composition is simply done with .. However, I don’t think any of the articles you’ve read meant this, either.

What they meant was “upgrading” function composition to work directly with “monadic functions”, i.e. functions of the form a -> m b. The technical term for such functions is Kleisli arrows, and indeed they can be composed with <=< or >=>. (Alternatively, you can use the Category instance, then you can also compose them with . or >>>.)

However, talking about arrows / categories tends to be confusing especially to beginners, just like point-free definitions of ordinary functions are often confusing. Luckily, Haskell allows us to express functions also in a more familiar style that focuses on the results of functions, rather the functions themselves as abstract morphisms. It’s done with lambda abstraction: instead of

q = h . g . f

you may write

q = (\x -> (\y -> (\z -> h z) (g y)) (f x))

…of course the preferred style would be (this being only syntactic sugar for lambda abstraction!)&ddagger;

q x = let y = f x
          z = g y
      in h z

Note how, in the lambda expression, basically composition was replaced by application:

q = \x -> (\y -> (\z -> h z) $ g y) $ f x

Adapted to Kleisli arrows, this means instead of

q = h <=< g <=< f

you write

q = \x -> (\y -> (\z -> h z) =<< g y) =<< f x

which again looks of course much nicer with flipped operators or syntactic sugar:

q x = do y <- f x
         z <- g y
         h z

So, indeed, =<< is to <=< like $ is to .. The reason it still makes sense to call it a composition operator is that, apart from “applying to values”, the >>= operator also does the nontrivial bit about Kleisli arrow composition, which function composition doesn’t need: joining the monadic layers.


The reason this works is that Hask is a cartesian closed category, in particular a well-pointed category. In such a category, arrows can, broadly speaking, be defined by the collection of all their results when applied to simple argument values.

&ddagger;@adamse remarks that let is not really syntactic sugar for lambda abstraction. This is particularly relevant in case of recursive definitions, which you can’t directly write with a lambda. But in simple cases like this here, let does behave like syntactic sugar for lambdas, just like do notation is syntactic sugar for lambdas and >>=. (BTW, there’s an extension which allows recursion even in do notation… it circumvents the lambda-restriction by using fixed-point combinators.)

Leave a Comment