12
Kleisli[M,A,B]

Sclalaz Kleisli の使い方

Embed Size (px)

Citation preview

Kleisli[M,A,B]

Kleisli[M,A,B]

A => M[B]

なる関数をラップするもの

例えばOption( 1 ) flatMap { i => Some( i * 2 ) // ここが Kleisli } flatMap { i => Some( i + 10 ) // ここも }

>>> Some( 12 )

for だとfor { i <- Option( 1 ) j <- Some( i * 2 ) k <- Some( j + 10 ) } yield k

>>> Some( 12 )

Kleisli を使うと

flatMap (for) を使わないで

処理をつなげて書ける

// andThen のイメージ val f = ( _: Int + 2 ) andThen ( _: Int * 10) f(1)

>>> 30

Kleisli をつくるdef multi(k: Int): Kleisli[Option, Int, Int] = Kleisli { a: Int => Some(a * k) }

def plus(k: Int): Kleisli[Option, Int, Int] = Kleisli { a: Int => Some(a + k) }

どちらも中身は Int => Option[Int] の関数

書き換えるとbefore

Option( 1 ) flatMap { i => Some( i * 2 ) } flatMap { i => Some( i + 10 ) }

afterOption(1) >>= multi(2) >>= plus(10)

>>> Some(12)

>=> で合成もできるval combined: Kleisli[Option, Int, Int] = { multi(2) >=> plus(10) >=> multi(3) }

Option(1) >>= combined

>>> Some(36)

<=< 逆向きの合成(compose) もあります

使用例

AMoAd では、広告候補のフィルタ処理で使っています

普通の filterList(1,2,3,4).filter( _ > 2 )

>>> List(3,4)

flatMap 版 filterList(1,2,3,4) flatMap { case i if i > 2 => List(i) case _ => Nil }

>>> List(3,4)

Kleisli 版 filter// kleisli の filter をつくる val filterK: Kleisli[List, Int, Int] = Kleisli { i: Int => if ( i > 2 ) List(i) else Nil }

List(1,2,3,4) >>= filterK

>>> List(3,4)

// 候補の取得 val ads: List[Ad] = getAds

// フィルタの合成 val allFilter: Kleisli[List, Ad, Ad] = budjetFilter >=> // 軽い処理を前に blacklistFilter >=> userAgentFilter >=> categroyFilter // 重い処理を後に

// フィルタの適用 val filteredAds: List[Ad] = ads >>= allfilter

フィルタの理由を持たせるため、\/ 型を使います// 型パラメータを1つにする type AppEither[T] = \/[String, T]

// List の代わりに AppEither で包む val adFilter: Kleisli[AppEither, Ad, Ad] = Kleisli { ad => if ( isValid(ad) ) \/-( ad ) else -\/( "filtered!!!" ) }

// 適用する val ads: List[Ad] = getAds val filtered = ads.map( adFilter )

>>> filtered: List[AppEither[Ad]]