LYAHFGG:
第13章でモナド則を紹介したとき、
<=<
関数は関数合成によく似ているど、普通の関数a -> b
ではなくて、a -> m b
みたいなモナディック関数に作用するのだよと言いました。
Cats には Kleisli と呼ばれる A => M[B]
という型の関数に対する特殊なラッパーがある:
/**
* 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)
}
Kleisli()
コンストラクタを使って Kliesli
値を構築する:
import cats._, cats.data._, cats.syntax.all._
val f = Kleisli { (x: Int) => (x + 1).some }
// f: Kleisli[Option, Int, Int] = Kleisli(run = <function1>)
val g = Kleisli { (x: Int) => (x * 100).some }
// g: Kleisli[Option, Int, Int] = Kleisli(run = <function1>)
compose
を使って関数を合成すると、右辺項が先に適用される。
4.some >>= (f compose g).run
// res0: Option[Int] = Some(value = 401)
andThen
を使うと、左辺項が先に適用される:
4.some >>= (f andThen g).run
// res1: Option[Int] = Some(value = 500)
compose
と andThen
は関数の合成同様に動作するが、モナディックなコンテキストを保持するのが違いだ。
Kleisli には、モナディック関数を別のアプリカティブ・ファンクターに持ち上げる lift
のような面白いメソッドがいくつかある。
と思って使ってみたけども、壊れている事に気付いたので、これが修正版だ #354:
def lift[G[_]](implicit G: Applicative[G]): Kleisli[λ[α => G[F[α]]], A, B] =
Kleisli[λ[α => G[F[α]]], A, B](a => Applicative[G].pure(run(a)))
使ってみる:
{
val l = f.lift[List]
List(1, 2, 3) >>= l.run
}
// res2: List[Option[Int]] = List(
// Some(value = 2),
// Some(value = 3),
// Some(value = 4)
// )