Kleisli vs flapMap Sequencing

Viewed 415

Checking at Kleisli definition,

in Cats, and Functional and Reactive Domain Modelling

However I'm not yet able to graps the usefulness of it. If we talk about the case of composing Monadic function, as in function that return monad i.e. A => F[B], i don't see what it actually add to simply sequencing a chain of

flatMap[A, B](ma: F[A])(f: A => F[B]): F[B]

Indeed, being able to chain the above is similar to

If you have a function f: A => F[B] and another function g: B => F[C], where F is a monad, then you can compose them to get A => F[C]

What is it that i am not seeing that is the real added value of "Kleisli" ?

2 Answers

Kleisli is simply a name for a function of shape A => F[B].

We could say that flatMap and Kleisli revolve around a similar idea and are tied to similar concepts, but they are not the same thing. Neither one "adds value upon the other one". Here's an example of their connection:

Monad can be defined in several different, but equally powerful ways. One is using unit + flatMap, with its laws defined as:

  • left-identity law:

         unit(x).flatMap(f) == f(x)
    
  • right-identity law:

         m.flatMap(unit) == m
    
  • associativity law:

         m.flatMap(f).flatMap(g) == m.flatMap(x ⇒ f(x).flatMap(g))
    

Another way is using unit + compose, with its laws defined as:

  • left-identity law:

        unit.compose(f) == f
    
  • right-identity law:

         f.compose(unit) == f
    
  • associativity law:

         f.compose(g.compose(h)) == (f.compose(g)).compose(h)
    

In the above definitions, flatMap is the good old flatMap you know:

def flatMap: F[A] => (A => F[B]) => F[B]

and compose is the composition of Kleisli arrows:

def compose: (A => F[B]) => (B => F[C]) => A => F[C]

So basically it's all about terminology. They often pop up in a similar context, but they are not the same. They are simply names for two related, but different things.

In addition to the answer given by slouc, I think it useful to add that I have never really seen the term Kleisli used without the term composition appended to it. So, you might say that the real benefit of separating out Kleisli functions is in how they can be composed.

flatMap isn't a composition of functions. It is, instead, a sequencing of operations on data. But Kleisli composition (like composition of other functions) allows the creation of new functions from other functions following certain rules - as pointed out by slouc.

In Haskell, composition is done with the dot operator. So, if f: A => B and g: B => C, you can have:

h = g . f       // h: A => C

But if f and g are Kleisli functions (f: A => M[B] and g: B => M[C]) this doesn't work. This is where Kleisli composition comes in to play. You often see it defined as the 'fish' operator, >=>, or something similar. Using Kleisli composition you can have:

h = g >=> f      // h: A => M[C]

BTW, depending on the language or library, the order of g and f in the fish operator may be reversed. But the concept still applies. You are building a new function from two existing functions via composition. You can, later, apply this function to data and have the same result you would have with sequential applications of flatMap.

One other thing I should probably mention is that since Kleisli functions compose they form a proper category so you will also see the term Kleisli Category. It isn't all that important to a SW developer but I had to come to grips with it since I saw it often in documentation and blogs so I thought I would pass it on.

Related