Watch video

Video tutorial


Kleisli enables composition of functions that return a monadic value, for instance, an Option<Int> or an Either<String, Int>, without having functions take an Option or Either as a parameter.

For example, we have the function String.toInt() which can throw a NumberFormatException and we want to do a safe conversion like this:

import arrow.core.*
import arrow.mtl.Kleisli

val optionIntKleisli = Kleisli { str: String ->
  if (str.toCharArray().all { it.isDigit() }) Some(str.toInt()) else None

fun String.safeToInt(): Option<Int> {

Then when we use the function we have an Option<Int>

// None
// Some(1)



This function allows doing a conversion inside the Kleisli to the original input value before the Kleisli will be executed, creating a Kleisli with the input type of the conversion

optionIntKleisli.local { optStr :Option<String> -> optStr.getOrElse { "0" } }.run(None)
// Some(0)


The ap function transform the Kleisli into another Kleisli with a function as a output value.

import arrow.mtl.fix
import arrow.core.extensions.option.applicative.*
import arrow.core.extensions.option.monad.*

val intToDouble = {number:Int -> number.toDouble()}

val optionIntDoubleKleisli = Kleisli { str: String ->
  if (str.toCharArray().all { it.isDigit() }) Some(intToDouble) else None

optionIntKleisli.ap(Option.applicative(), optionIntDoubleKleisli).fix().run("1")
// Some(1.0)


The map function transform the Kleisli output value. { output -> output + 1 }.fix().run("1")
// Some(2)


flatMap is useful to map the Kleisli output into another kleisli

import arrow.mtl.fix

val optionDoubleKleisli = Kleisli { str: String ->
  if (str.toCharArray().all { it.isDigit() }) Some(str.toDouble()) else None

optionIntKleisli.flatMap(Option.monad(), { optionDoubleKleisli }).fix().run("1")
// Some(1.0)


You can use andThen to compose with another kleisli

import arrow.mtl.fix

val optionFromOptionKleisli = Kleisli { number: Int ->

optionIntKleisli.andThen(Option.monad(), optionFromOptionKleisli).fix().run("1")
// Some(2)

with another function

optionIntKleisli.andThen(Option.monad(), { number: Int -> Some(number+1) }).fix().run("1")
// Some(2)

or to replace the Kleisli result

optionIntKleisli.andThen(Option.monad(), Some(0)).fix().run("1")
// Some(0)

Do you like Arrow?

Arrow Org