sclalaz kleisli の使い方
TRANSCRIPT
例えばOption( 1 ) flatMap { i => Some( i * 2 ) // ここが Kleisli } flatMap { i => Some( i + 10 ) // ここも }
>>> 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]]