2011年12月13日火曜日

Applicative, ApplicativeBuilder

#expertpython に行く予定でしたが池袋駅で目黒へ行くお金すらなかったことに気づいて泣く泣く帰ることに・・・・・
鬱々ですが明日も頑張ります。

今日はApplicative。

Applicative

ApplicativeはApplyとPointedを継承します。
基本的にApplyはFunctorや、Pureと組み合わせて使うのでApplicativeのインスタンスであるといろいろな関数が使えるようになります。

case class ScalaChan[A](value: A)
implicit def ScalaChanEqual[A]: Equal[ScalaChan[A]] = equalA
implicit def ScalaChanShow[A]: Show[ScalaChan[A]] = showA
implicit lazy val ScalaChanPure: Pure[ScalaChan] = new Pure[ScalaChan] {
def pure[A](a: => A): ScalaChan[A] = ScalaChan(a)
}
implicit lazy val ScalaChanFunctor: Functor[ScalaChan] = new Functor[ScalaChan] {
def fmap[A, B](a: ScalaChan[A], f: A => B): ScalaChan[B] = ScalaChan(f(a.value))
}
implicit lazy val ScalaChanBind: Bind[ScalaChan] = new Bind[ScalaChan] {
def bind[A, B](a: ScalaChan[A], f: A => ScalaChan[B]): ScalaChan[B] = f(a.value)
}
implicit lazy val ScalaChanApply: Apply[ScalaChan] = FunctorBindApply
def f[A[_]: Applicative, B](a: A[B], f: B => B => B) = a <*> (a map f)
f[Option, Int](5.some, a => b => a |+| b) assert_=== Option(10)
f[ScalaChan, String](ScalaChan("Scalaz"), a => b => a |+| b) assert_=== ScalaChan("ScalazScalaz")

Pointed

FunctorとPureを継承しています。
Applicative, Monadがこれを継承します。

Applicativeスタイル

forでは

lazy val f: Int => Int => Int = i => j => (i ∏) |+| (j ∏)
val a = for {
i <- 2.some
j <- 100.some
} yield f(i)(j)
val x = for {
i <- nel(1, 2, 3)
j <- nel(4, 5, 6)
} yield f(i)(j)

と書けますが、Applicativeスタイルでは

val b = 100.some <*> (2.some map f)
val y = nel(4, 5, 6) <*> (nel(1, 2, 3) map f)
a assert_=== b
x assert_=== y

と書けます。

この書き方では、関数がカリー化されている必要があります。
<**>, <***>, <****>, <*****>を使うとカリー化の必要がなくなり、適用の順番が綺麗に書けます。

def f(i: Int, j: Int): Int = (i ∏) |+| (j ∏)
val b = (2.some <**> 100.some)(f)
val y = (nel(1, 2, 3) <**> nel(4, 5, 6))(f)
a assert_=== b
x assert_=== y


ApplicativeBuilder

これらの書き方以外にApplicativeBuilderを使ったものがあります。

val c = (2.some |@| 100.some)(f)
a assert_=== c
def g(i: Int, j: Int, k: Int): Int = i |+| j |+| k
(1.some |@| 2.some |@| 3.some)(g) assert_=== Option(6)
(((_: Int) |+| 1) |@| ((_: Int) |+| 2) |@| ((_: Int) |+| 3))(g).apply(5) assert_=== 21

このようにApplicativeは値をコンテナから取り出して関数にてきようするものです。
for yieldであの形になったのなら、Applicativeスタイルで書けるということをおぼえておけばいいのだと思います。

0 件のコメント:

コメントを投稿