LoginSignup
12
14

More than 5 years have passed since last update.

scalaz - Kleisli

Posted at

独習Scalaz - モナディック関数の合成のメモ。

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とか色々インスタンスもってるようだ。

12
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14