OptionはScalaプログラミングのキーパーツであり、Optionの捌き方の巧拙がScalaプログラミングの効率に直結します。
これと同様にMonoidはScalazプログラミングのキーパーツということができるかと思います。Monoidの捌き方の巧拙がScalazプログラミング、さらにはMonadicプログラミングの効率に直結することになるでしょう。
そして、ScalazのおいてOptionは代表的なMonoidです。つまり、OptionをMonoidとして捌いていく技法はScalazプログラミング、Monadicプログラミングにおいて最重要のテクニックといえるわけです。
というととても難しい技術のように見えますが、(背景の数学的な理論は別として)使い方はとても簡単で、さらに頻出のユースケースをカバーする非常に便利なイディオムです。
準備
説明の準備に以下の関数を定義します。
scala> def a: Option[Int] = Some(100) def a: Option[Int] = Some(100) a: Option[Int] scala> def b: Option[Int] = None def b: Option[Int] = None b: Option[Int]
よくある処理
プログラムを書いているとよく出てくるのが以下のような処理です。
def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = { lhsの中身とrhsの中身を加算する ただしlhsまたはrhsのどちらか一方がNoneの場合はSomeの方の値を使用する 両方がNoneの場合はNoneにする }
よくある間違い
この処理を書く場合、Optionを使ってMonadicに処理すると行けそうに思えます。そこで、次のような処理を書いたとしましょう。
def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = { d lhs.flatMap(x => rhs.map(y => x + y)) }
これは、for式を使って以下のように書くこともできます。
def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = { for (x <- lhs; y <- rhs) yield x + y }
さて、この結果ですが、以下のようになります。
scala> plus(a, a) res1: Option[Int] = Some(200) scala> plus(a, b) res3: Option[Int] = None scala> plus(b, a) res4: Option[Int] = None scala> plus(b, b) res2: Option[Int] = None
Monadicに処理した場合は、残念ながらいずれかのOptionがNoneだった場合に、結果もNoneになってしまうわけです。このような処理が適切なケースも多いわけですが、前述の「plus関数」の定義とは異なるのでこの実装は使えません。
Match式
「plus関数」をmatch式を使って実装すると以下のようになります。
def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = { (lhs, rhs) match { case (Some(l), Some(r)) => Some(l + r) case (Some(l), None) => Some(l) case (None, Some(r)) => Some(r) case (None, None) => None } }
このようなOptionの組み合わせごとに処理を分けるmatch式はScalaプログラミングをしているとよく出てくるのではないでしょうか?しかも、かなり野暮ったい感じなのでこれを簡単に書けると好都合です。
Monoid
前出のmatch式による処理をScalazのMonoidで書くと以下になります。複雑なmatch式を演算子「|+|」のみで記述できています。
ScalazではOptionのMonoid演算として、前述のmatch式相当の演算を割り当てているわけですね。
def plus(lhs: Option[Int], rhs: Option[Int]): Option[Int] = { lhs |+| rhs }
この「plus」関数の実行結果は以下になります。
scala> plus(a, a) res9: Option[Int] = Some(200) scala> plus(a, b) res10: Option[Int] = Some(100) scala> plus(b, a) res11: Option[Int] = Some(100) scala> plus(b, b) res13: Option[Int] = None
おさらい
おさらいの意味でOptionに対してMonoidの演算子「|+|」を適用して演算を行った結果を以下に示します。
scala> 1.some |+| 2.some 1.some |+| 2.some res40: Option[Int] = Some(3) scala> 1.some |+| none[Int] 1.some |+| none[Int] res54: Option[Int] = Some(1) scala> none[Int] |+| 2.some none[Int] |+| 2.some res42: Option[Int] = Some(2) scala> none[Int] |+| none[Int] none[Int] |+| none[Int] res53: Option[Int] = None
参考
諸元
- Scala 2.10.4
- Scalaz 7.0.6
0 件のコメント:
コメントを投稿