Scalaz には Kleisli と呼ばれる A => M[B] という型の関数に対する特殊なラッパーがある:
定義
さっそくKleisliの定義をみてみる。
final case class Kleisli[M[_], A, B](run: A => M[B])
使い方をみてみよう。
使い方
Kleisli { a: Int => Option(a + "!") } run 1
// Some(1!)
たしかに A => M[B]
だ。
Kleisliは、関数に変換できるようなimplicitが定義されている。
val f: Int => Option[String] = Kleisli { a: Int => (a + "!").some }
f(1) // Some(1!)
ちなみにOptionだけはalias =?>
が定義されている。
val k: Int =?> String = Kleisli { a: Int => Option(a + "!") }
// Kleisli[Option, Int, String]
インスタンスの関数
ひたすらに関数を使ってみる。
andThen (alias: >=>)
Kleisli[M, A, B] >=> Kleisli[M, B, C]
だ
Kleisli { a: Int => Option(a + "!") } >=> Kleisli { b: String => Option(b + "?") } run 1
// Some(1!?)
compose (alias: <=<)
Kleisli { a: String => Option(a + "!") } <=< Kleisli { a: Int => Option(a + "?") } run 1
// Some(1?!)
>==>: 関数だけ渡せばandThenと同じ
Kleisli { a: Int => Option(a + "!") } >==> { b: String => Option(b + "?") } run 1
// Some(1!?)
<==<: 関数を渡すだけでcomposeと同じ
Kleisli { a: String => Option(a + "!") } <==< { a: Int => Option(a + "?") } run 1
// Some(1?!)
traverse: これは F[A] => M[F[B]]
な関数だ
Kleisli { a: Int => Option(a + "!") } traverse List(1)
// Some(List(1!))
=<<: 値を取り出してKeisliに適用してくれる M[A] => M[B]
な関数だ
Kleisli { a: Int => Option(a + "!") } =<< Option(1)
// Some(1!)
map: B => C
にするいつもの関数だ
Kleisli { a: Int => Option(a + "!") } map { _ + "?" } run 1
// Some(1!?)
flatMap: B => Kleisli[M, A, C]
にするいつもの関数だ
Kleisli { a: Int => Option(a + "!") } flatMap { b: String => Kleisli { a: Int => Option("c") } } run 1
// c
mapK: M[B] => N[C]
にする関数だ。Nは1つの型をとる何か。
Kleisli { a: Int => Option(a + "!") } mapK { mb: Option[String] => mb.toList } run 1
// List(1?)
flatMapK: B => M[C]
にする関数だ
Kleisli { a: Int => Option(a + "!") } flatMapK { b: String => Option("c") } run 1
// c
え? mapKは M[B] => N[C]
だから、flatMapKは M[B] => Kleisli[N, A, C]
だと思ったんだけど...?
state: StateTに変換
Kleisli { (_:Int).some }.state
// StateT[Option,Int,Int]
liftMK: モナドの合成
Kleisli { (_:Int).some }.liftMK[ListT]
// ListT[Option,A],Int,Int]
local: 最初に渡した関数を適用する
Kleisli { a:Int => (a + 2).some } local { a:Int => a * 2 } run 1
// Some(4)
endo: TODO
liftF: TODO
オブジェクトの関数
kleisliU: 型を2つとる型でも
Kleisli.kleisliU { a: Int => Writer("ok", a) } run 1
// WriterT((ok,1))
これは便利だ...。
ask: pointみたいなものだ
Kleisli.ask[Option, Int]
// Kleisli[Option,Int,Int]
local: インスタンスの関数であったのと同じ感じだ。最初に渡した関数を評価する。
Kleisli.local({a: Int => a + 2})(Kleisli { a: Int => (a * 2).some }) run 1
// Some(6)
liftについて
liftやunliftは複雑だからよく見てみることにしよう。
lift: Mをliftする関数だ。Applicativeだったらなんでもいいようだ。
val k= Kleisli { a: Int => Option(a + "!") }.lift[List]
// Kleisli[List[Option[_]], Int, String]
k(1)
// List(Some(1!))
unlift: liftの逆だ。
val k = Kleisli { a: Int => Option(a + "!") }.lift[Tree]
// Kleisli[Tree[Option], Int, String] MをTreeにlift
k.unlift[Tree, Option]
// Kleisli[Option, Int, String] unliftされた
こいつはちょっと複雑だ。
def unlift[N[_], FF[_]](implicit M: Comonad[N], ev: this.type <~< Kleisli[λ[α => N[FF[α]]], A, B]): Kleisli[FF, A, B]
Comonadのimplicitが必要なのでTreeにした。他にもComonadにはWriterやFunction1などの定義があった。
<~<
はLiskov内の関数だがよく分かってない..。なんでここで必要なんだろうか。難しい...
というかComonadのインスタンス少ないのか..haskellもそんなもんなんだろうか...分からん。
unliftId: unliftのId固定版だ
val k = Kleisli[({type λ[A]=Writer[String, A]})#λ, Int, Int] { x: Int => Writer("ok", x) }
// Kleisli[WriterT[Id, String, A], Int, Int] (IdとWriterTが合成された状態だ)
val k2 = k.unliftId[({type λ[A]=Writer[String,A]})#λ]
// Kleisli[Id, Int, Int] (Writerをunliftできた)
k2 run 1
// 1: Id[Int]
lift/unliftできてる。
他に
あとKleisliはMonoidでもあるようだ。
val k = Kleisli { (_:Int).some }
k |+| k |+| k run 1
// Some(3)
他にもApplyとか色々インスタンスもってるようだ。