これは Scalaz Advent Calendar 2012 の16日目です。
kazu yamamoto さんの Applicativeのススメ
より引用
簡潔に結論を述べると、
foo = do
a <- m1
b <- m2
return (f a b)
のようなコードを書きたくなったらfoo = f <$> m1 <*> m2
と書きましょうということ。合い言葉は、「do と return をなくせ!」です。
Scala(z) でも大体同じですよね!というわけで(?)
これ↓
Arbitrary(for { t1 <- arbitrary[T1] t2 <- arbitrary[T2] t3 <- arbitrary[T3] t4 <- arbitrary[T4] t5 <- arbitrary[T5] } yield (t1,t2,t3,t4,t5))
が、こう書き換えられますよね↓
Arbitrary(Apply[Gen].apply5( arbitrary[T1], arbitrary[T2], arbitrary[T3], arbitrary[T4], arbitrary[T5] )(Tuple5.apply))
と、Scalacheck の コードみてたら思いついたので、書き換えてみただけです。
https://github.com/xuwei-k/scalacheck/commit/75c818a8419389e66aff61325b157021f9e7b9b2
変数が5つも減ってます。*1 ちなみに、ApplicativeBuilder を使った他の書き方もあります。*2
(arbitrary[T1] |@| arbitrary[T2] |@| arbitrary[T3] |@| arbitrary[T4] |@| arbitrary[T5])(Tuple5.apply)
もしくは
(arbitrary[T1] |@| arbitrary[T2] |@| arbitrary[T3] |@| arbitrary[T4] |@| arbitrary[T5]).tupled
追記:
さらに他の書き方もあります
^^^^(arbitrary[T1],arbitrary[T2],arbitrary[T3],arbitrary[T4],arbitrary[T5])(Tuple5.apply)
そして上記の書き換えをしたあと、さらに tuple2 から tuple5 というメソッドが Apply にあることに気づき
https://github.com/scalaz/scalaz/blob/v7.0.0-M6/core/src/main/scala/scalaz/Apply.scala#L92-L99
tuple2 から tuple5 を使って書き換えたのが以下
https://github.com/xuwei-k/scalacheck/commit/7bec59f396611f2bc22ffe4abf7d57351fdd1656
Arbitrary(Apply[Gen].tuple5( arbitrary[T1], arbitrary[T2], arbitrary[T3], arbitrary[T4], arbitrary[T5] ))
で、applyNやliftNは現状( scalaz 7.0.0-M6 ) 12まであるのに、tupleNは5までしかなくて全部書き換えられない・・・(´・ω・`)
ちなみに、もちろんこれをScalacheckにpull requestはしません。*3
そして、上記の書き換えを行うには、本当は org.scalacheck.Genの Applicative *4のインスタンスを定義しなければいけないんですが、実は Scalaz には scalacheck-binding というモジュールがあって(ry
という話を書こうかと思ったけど、長くなったし、別の話なので、またあとで書きます