Here are two benefits as examples—I'm sure you could come up with others.
First, it can be useful to abstract over different arrows, such as Kleisli[M, ?, ?]
and ? => ?
. For example, I can write a generic function that will apply an endomorphism a certain number of times.
def applyX10[Arr[_, _]: Category, A](f: Arr[A, A]) =
List.fill(10)(Endomorphic(f)).suml
Now I can use this on e.g. Int => Int
or Kleisli[Option, Int, Int]
:
val f = (_: Int) + 1
val k = Kleisli.kleisli[Option, Int, Int] {
case i if i % 2 == 0 => Some(i * 3)
case _ => None
}
And then:
scala> applyX10(f).run(1)
res0: Int = 11
scala> applyX10[=?>, Int](k).run(2)
res1: Option[Int] = Some(118098)
(Note that A =?> B
is just an alias for Kleisli[Option, A, B]
.)
Second, the fact that Kleisli[F, ?, ?]
has a monad instance if F
does can also be useful. See for example my answer here for a demonstration of how you can use monadic composition with ReaderT
, which is just an alias for Kleisli
.
Kleisli
in general as an abstraction over different arrows (2) special cases: e.g.ReaderT
, reader monad, etc. – Eliza