ScalazのOptionはMonoidとしても定義されています。
「Scalazの型クラス」で説明したように型クラスMonoidは型クラスZeroと型クラスSemigroupを継承しています。
Semigroup
Monoidの性質のうちSemigroupが提供しているのは演算子|+|によるモノイド演算です。
Optionは、格納するオブジェクトがMonoidである場合、Monoidとして動作します。
両方のOptionがSomeの場合、格納されているMonoid同士に演算子|+|で演算した結果がSomeに格納されます。
scala> 1.some |+| 2.some res10: Option[Int] = Some(3)
どちらか一方がNoneの場合は、Someの方が演算子|+|の演算結果となります。
scala> 1.some |+| none res11: Option[Int] = Some(1) scala> none |+| 2.some res12: Option[Int] = Some(2)
両方共Noneの場合はNoneになります。
scala> none[Int] |+| none[Int] res14: Option[Int] = None
Zero
Monoidはmzero関数で単位元(零元)を生成することができます。
Optionの場合は以下になります。
scala> mzero[Option[Int]] res19: Option[Int] = None
Option[Int]の単位元としては、「Some(0)」が返ってきた方がよい場合もあると思いますが、現実装ではNoneが返ります。
使ってみる
OptionがMonoidであることを利用すると、以下のように直接sumrメソッドを適用することができます。
scala> List(1.some, 2.some, 3.some, 4.some).sumr res32: Option[Int] = Some(10) scala> List(1.some, 2.some, none, 4.some).sumr res20: Option[Int] = Some(7)
OptionのMonoidの場合はOptionのコンテナ側の畳込みがOR演算的な処理になるので、Noneは無視されてSomeのみが畳み込まれます。
sequence
比較のためにsequenceメソッドを使ってみましょう。
scala> List(1.some, 2.some, 3.some, 4.some).sequence[Option, Int].map(_.sum) res28: Option[Int] = Some(10) scala> List(1.some, 2.some, none, 4.some).sequence[Option, Int].map(_.sum) res27: Option[Int] = None
この場合は、1つでもNoneがあると結果もNoneになります。Optionのコンテナ側の畳込みがAND演算的な処理になります。
Applicative
Applicative Functorとして畳込む場合も、sequenceメソッドと同様にAND演算になります。
scala> List(1.some, 2.some, 3.some, 4.some).foldRight(0.some) { (x, a) => (x |@| a)(_ + _)} res34: Option[Int] = Some(10) scala> List(1.some, 2.some, none, 4.some).foldRight(0.some) { (x, a) => (x |@| a)(_ + _)} res29: Option[Int] = None
mzeroは注意が必要
OptionをMonoidとして畳み込む時に注意が必要なのはmzero関数の結果がNoneになってしまう点です。このため、以下のようにApplicativeを使った畳込みの初期値に指定すると、畳込み結果が必ずNoneになってしまいます。
scala> List(1.some, 2.some, 3.some, 4.some).foldRight(mzero[Option[Int]]) { (x, a) => (x |@| a)(_ |+| _)} res31: Option[Int] = None scala> List(1.some, 2.some, none, 4.some).foldRight(mzero[Option[Int]]) { (x, a) => (x |@| a)(_ |+| _)} res30: Option[Int] = None
ノート
Optionのようなコンテナは、畳込みのコンテナ側の演算としてOR演算とAND演算の選択肢があります。
OptionをMonoidとして畳み込むとOR演算、sequenceやApplicative Functorを使うとAND演算になることが分かりました。この振舞いの違いは、用途別のイディオムとして活用できますね。
諸元
- Scala 2.9.2
- Scalaz 6.0.4
0 件のコメント:
コメントを投稿