前回説明した通り、Scalaz 7のOptionはTagを用いてMonoid演算の振舞いを制御できます。
Optionでは以下の4つのTagが定義されています。
- Tags.First
- Tags.Last
- Tags.Max
- Tags.Min
Tagを指定していない場合を加えると、Monoid演算で5種類の振舞いを選択することができます。
デフォルト
まずデフォルトの動きを確認しましょう。
以下のOptionを定義します。
val a = 1.some val b = 2.some val c = 3.some val n = none[Int]
Monoid演算の結果は以下になります。|+|は2つのMonoid間のMonoid演算、ListのsumrメソッドはMonoidの畳込みです。
scala> a |+| b res65: Option[Int] = Some(3) scala> n |+| b res66: Option[Int] = Some(1) scala> a |+| n res67: Option[Int] = Some(2) scala> n |+| n res68: Option[Int] = None scala> List(a, b, c).sumr res69: Option[Int] = Some(6) scala> List(n, b, c).sumr res70: Option[Int] = Some(5) scala> List(a, n, c).sumr res71: Option[Int] = Some(4) scala> List(a, b, n).sumr res72: Option[Int] = Some(3)
Tags.First
次はTags.Firstのタグ付けをした場合です。
val a: Option[Int] @@ Tags.First = Tag(1.some) val b: Option[Int] @@ Tags.First = Tag(2.some) val c: Option[Int] @@ Tags.First = Tag(3.some) val n: Option[Int] @@ Tags.First = Tag(none)
Monoid演算の結果は以下になります。Monoid演算の結果、最初に有効なOptionが選択されていることが分かります。
scala> a |+| b res37: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(1) scala> a |+| n res40: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(1) scala> n |+| b res39: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(2) scala> n |+| n res41: scalaz.@@[Option[Int],scalaz.Tags.First] = None scala> List(a, b, c).sumr res48: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(1) scala> List(n, b, c).sumr res38: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(2) scala> List(a, n, c).sumr res55: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(1) scala> List(a, b, n).sumr res56: scalaz.@@[Option[Int],scalaz.Tags.First] = Some(1)
Tagで型を指定
First.Tagの指定をTagメソッド(scalaz.Tagのapplyメソッド)で行うこともできます。
val a = Tag[Option[Int], Tags.First](1.some) val b = Tag[Option[Int], Tags.First](2.some) val c = Tag[Option[Int], Tags.First](3.some) val n = Tag[Option[Int], Tags.First](none)
FirstOption
OptionのTags.First用に専用の型としてFirstOptionが定義されています。Scalaz 6のFirstOptionは専用のトレイトでしたが、似たような使い方ができます。
val a: FirstOption[Int] = Tag(1.some) val b: FirstOption[Int] = Tag(2.some) val c: FirstOption[Int] = Tag(3.some) val n: FirstOption[Int] = Tag(none)
firstメソッド
Scalaz 7のOptionはFirstOptionを取得するfirstメソッドを提供しています。Scalaz 6のfstメソッドに対応します。
val a = 1.some.first val b = 2.some.first val c = 3.some.first val n = none[Int].first
Tags.Last
次はTags.Lastのタグ付けをした場合です。
val a: Option[Int] @@ Tags.Last = Tag(1.some) val b: Option[Int] @@ Tags.Last = Tag(2.some) val c: Option[Int] @@ Tags.Last = Tag(3.some) val n: Option[Int] @@ Tags.Last = Tag(none)
Monoid演算の結果は以下になります。Monoid演算の結果、最後に有効なOptionが選択されていることが分かります。
scala> a |+| b res73: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(2) scala> a |+| n res74: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(1) scala> n |+| b res75: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(2) scala> n |+| n res76: scalaz.@@[Option[Int],scalaz.Tags.Last] = None scala> List(a, b, c).sumr res77: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(3) scala> List(n, b, c).sumr res78: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(3) scala> List(a, n, c).sumr res79: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(3) scala> List(a, b, n).sumr res80: scalaz.@@[Option[Int],scalaz.Tags.Last] = Some(2)
Tagで型を指定
Last.Tagの指定をTagメソッド(scalaz.Tagのapplyメソッド)で行うこともできます。
val a = Tag[Option[Int], Tags.Last](1.some) val b = Tag[Option[Int], Tags.Last](2.some) val c = Tag[Option[Int], Tags.Last](3.some) val n = Tag[Option[Int], Tags.Last](none)
LastOption
OptionのTags.Last用に専用の型としてLastOptionが定義されています。Scalaz 6のLastOptionは専用のトレイトでしたが、似たような使い方ができます。
val a: LastOption[Int] = Tag(1.some) val b: LastOption[Int] = Tag(2.some) val c: LastOption[Int] = Tag(3.some) val n: LastOption[Int] = Tag(none)
lastメソッド
Scalaz 7のOptionはLastOptionを取得するlastメソッドを提供しています。Scalaz 6のsndメソッドに対応します。
val a = 1.some.last val b = 2.some.last val c = 3.some.last val n = none[Int].last
Tags.Max
次はTags.Firstのタグ付けをした場合です。
val a: Option[Int] @@ Tags.Max = Tag(1.some) val b: Option[Int] @@ Tags.Max = Tag(2.some) val c: Option[Int] @@ Tags.Max = Tag(3.some) val n: Option[Int] @@ Tags.Max = Tag(none)
Monoid演算の結果は以下になります。Monoid演算の結果、最大値の値を持つOptionが選択されていることが分かります。
scala> a |+| b res81: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(2) scala> n |+| b res82: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(1) scala> a |+| n res83: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(2) scala> n |+| n res84: scalaz.@@[Option[Int],scalaz.Tags.Max] = None scala> List(a, b, c).sumr res85: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(3) scala> List(n, b, c).sumr res86: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(3) scala> List(a, n, c).sumr res87: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(3) scala> List(a, b, n).sumr res88: scalaz.@@[Option[Int],scalaz.Tags.Max] = Some(2)
Tagで型を指定
Fisrt.Tagの指定をTagメソッド(scalaz.Tagのapplyメソッド)で行うこともできます。
val a = Tag[Option[Int], Tags.Max](1.some) val b = Tag[Option[Int], Tags.Max](2.some) val c = Tag[Option[Int], Tags.Max](3.some) val n = Tag[Option[Int], Tags.Max](none)
MaxOption
OptionのTags.Max用に専用の型としてMaxOptionが定義されています。
val a: MaxOption[Int] = Tag(1.some) val b: MaxOption[Int] = Tag(2.some) val c: MaxOption[Int] = Tag(3.some) val n: MaxOption[Int] = Tag(none)
OptionのTags.Max向けに、firstメソッドに対応するメソッドはないようです。
Tags.Min
次はTags.Minのタグ付けをした場合です。
val a: Option[Int] @@ Tags.Min = Tag(1.some) val b: Option[Int] @@ Tags.Min = Tag(2.some) val c: Option[Int] @@ Tags.Min = Tag(3.some) val n: Option[Int] @@ Tags.Min = Tag(none)
Monoid演算の結果は以下になります。Monoid演算の結果、最も小さな値としてSome(1)またはNoneが選択されていることが分かります。OptionのTags.Minは、最小値として値が設定されないNoneを選択する仕様になっているようです。ボクの直感ではNoneでない場合は、Some(1)を最小値とすると考えていたので、個人的に注意が必要な仕様と認識しました。
scala> a |+| b res89: scalaz.@@[Option[Int],scalaz.Tags.Min] = Some(1) scala> n |+| b res90: scalaz.@@[Option[Int],scalaz.Tags.Min] = None scala> a |+| n res91: scalaz.@@[Option[Int],scalaz.Tags.Min] = None scala> n |+| n res92: scalaz.@@[Option[Int],scalaz.Tags.Min] = None scala> List(a, b, c).sumr res93: scalaz.@@[Option[Int],scalaz.Tags.Min] = None scala> List(n, b, c).sumr res94: scalaz.@@[Option[Int],scalaz.Tags.Min] = None scala> List(a, n, c).sumr res95: scalaz.@@[Option[Int],scalaz.Tags.Min] = None scala> List(a, b, n).sumr res96: scalaz.@@[Option[Int],scalaz.Tags.Min] = None
Tagで型を指定
Min.Tagの指定をTagメソッド(scalaz.Tagのapplyメソッド)で行うこともできます。
val a = Tag[Option[Int], Tags.Min](1.some) val b = Tag[Option[Int], Tags.Min](2.some) val c = Tag[Option[Int], Tags.Min](3.some) val n = Tag[Option[Int], Tags.Min](none)
MinOption
OptionのTags.Min用に専用の型としてMinOptionが定義されています。
val a: MinOption[Int] = Tag(1.some) val b: MinOption[Int] = Tag(2.some) val c: MinOption[Int] = Tag(3.some) val n: MinOption[Int] = Tag(none)
OptionのTags.Min向けに、firstメソッドに対応するメソッドはないようです。
ノート
関数型プログラミングのポイントの一つは、アルゴリズムを様々なデータ構造に対していかに再利用していくのかという点にあると思います。別の言い方をするとアルゴリズムとデータ構造を疎結合するためのメカニズムが論点になります。
Monoidという抽象および型クラスによる実行メカニズムは、Monoidを操作するアルゴリズムを、操作対象のデータ構造から疎結合にするために大きく寄与します。特にfold系の畳込みとMonoidの組合せは、関数型プログラミングにおける最重要イディオムの一つということができるでしょう。
ここまでがScalaz 6の成果ですが、Scalaz 7ではTagというメカニズムが加わり、さらにアルゴリズムを再利用できる範囲が広がりました。OptionのようなコンテナをMonoidとして使用する場合、コンテナに格納されているオブジェクトに対するMonoid演算は複数の可能性が考えられます。Scalaz 6のMonoidではその中の一つの選択(オブジェクト同士をMonoid演算する)に決め打ちにしていたわけですが、Scalaz 7ではTagの導入により、Tagの指定で選択を細かく指定することができるようになりました。
アルゴリズム側は全く意識することなく、Monoidのパラメータを指定するときのTag付けのみで、Monoid演算の細かな振舞いを切り替えることができます。このメカニズムにより、Monoidを操作対象とするアルゴリズムの適用範囲が更に広がることになります。
追記(2012-08-28)
Kenji YoshidaさんからMaxOptionとMinOptionが存在している旨の、ご指摘あったので、内容を修正しました。どうもありがとうございます。
また、ねこはるさんからご指摘のあったTags.First, Tags.Last, Tags.Max, Tags.Minメソッドは「Option(16) - First, Last, Max, Min その2」で情報を追加しました。
諸元
- Scala 2.10.0-M6
- Scalaz 7.0.0-M2
> OptionのTags.Maxでは、FirstOptionに対応する型やfirstメソッドに対応するメソッドはないようです。
返信削除First,Last,Max,Min に関して、package object にすべて alias が定義されてます
https://github.com/scalaz/scalaz/blob/v7.0.0-M2/core/src/main/scala/scalaz/package.scala#L156-159
情報有り難うございます。
返信削除確かにMaxOption,MinOptionのaliasはありますね。訂正しておきます。