Composing monadic functions 

LYAHFGG:

When we were learning about the monad laws, we said that the <=< function is just like composition, only instead of working for normal functions like a -> b, it works for monadic functions like a -> m b.

In Cats there’s a special wrapper for a function of type A => F[B] called Kleisli:

/**
 * Represents a function `A => F[B]`.
 */
final case class Kleisli[F[_], A, B](run: A => F[B]) { self =>

  ....
}

object Kleisli extends KleisliInstances with KleisliFunctions

private[data] sealed trait KleisliFunctions {

  def pure[F[_], A, B](x: B)(implicit F: Applicative[F]): Kleisli[F, A, B] =
    Kleisli(_ => F.pure(x))

  def ask[F[_], A](implicit F: Applicative[F]): Kleisli[F, A, A] =
    Kleisli(F.pure)

  def local[M[_], A, R](f: R => R)(fa: Kleisli[M, R, A]): Kleisli[M, R, A] =
    Kleisli(f andThen fa.run)
}

We can use the Kleisli() constructor to construct a Kliesli value:

scala> :paste
// Entering paste mode (ctrl-D to finish)
object Catnip {
  implicit class IdOp[A](val a: A) extends AnyVal {
    def some: Option[A] = Some(a)
  }
  def none[A]: Option[A] = None
}
import Catnip._

// Exiting paste mode, now interpreting.

defined object Catnip
import Catnip._

scala> import cats._, cats.instances.all._
import cats._
import cats.instances.all._

scala> import cats.data.Kleisli
import cats.data.Kleisli

scala> val f = Kleisli { (x: Int) => (x + 1).some }
f: cats.data.Kleisli[Option,Int,Int] = Kleisli(<function1>)

scala> val g = Kleisli { (x: Int) => (x * 100).some }
g: cats.data.Kleisli[Option,Int,Int] = Kleisli(<function1>)

We can then compose the functions using compose, which runs the right-hand side first:

scala> import cats.syntax.flatMap._
import cats.syntax.flatMap._

scala> 4.some >>= (f compose g).run
res0: Option[Int] = Some(401)

There’s also andThen, which runs the left-hand side first:

scala> 4.some >>= (f andThen g).run
res1: Option[Int] = Some(500)

Both compose and andThen work like function composition but note that they retain the monadic context.

lift method 

Kleisli also has some interesting methods like lift, which allows you to lift a monadic function into another applicative functor. When I tried using it, I realized it’s broken, so here’s the fixed version #354:

  def lift[G[_]](implicit G: Applicative[G]): Kleisli[λ[α => G[F[α]]], A, B] =
    Kleisli[λ[α => G[F[α]]], A, B](a => Applicative[G].pure(run(a)))

Here’s how we can use it:

scala> val l = f.lift[List]
l: cats.data.Kleisli[[α]List[Option[α]],Int,Int] = Kleisli(<function1>)

scala> List(1, 2, 3) >>= l.run
res0: List[Option[Int]] = List(Some(2), Some(3), Some(4))