Optionから値を取り出すイディオムです。
Optionから値を取り出す処理として以下の2つのコーディングパターンを挙げました。
- Option[A]からOption[B]に変換
- Option[A]からBに変換
前回は、後者のOption[A]からBへの変換について、基本的なイディオムを紹介しました。
条件 | 結果 | 演算 |
---|---|---|
Option[A]がSome[A] | B | AからBを計算 |
Option[A]がNone | B | デフォルト値 |
今回は、Option[A]がNoneの場合だけでなく、Some[A]の値が条件を満たさない場合も、デフォルト値を返すようにします。
条件 | 結果 | 演算 |
---|---|---|
Option[A]に有効な値が入っている | B | AからBを計算 |
Option[A]に無効な値が入っている | B | デフォルト値 |
Option[A]がNone | B | デフォルト値 |
以下では、Option[Int]からStringへの変換を例に考えてみます。ただし、Intは0以上のものが有効という条件を追加します。Optionに入っているIntが0以上の場合、Stringが処理結果となります。一方、0未満の場合は無効となり空文字列「""」が処理結果となります。またOption[A]がNoneの場合も空文字列「""」が処理結果となります。
Java風
if式でOption#isDefinedを使って値の有無を判定します。isDefinedAtメソッドが一回、getメソッドが二回が泣き別れになってしまいます。
def f(a: Option[Int]): String = { if (a.isDefined && a.get >= 0) a.get.toString else "" }
Scala風
match式を使うと以下のようになります。こちらの方が綺麗ですね。
def f(a: Option[Int]): String = { a match { case Some(b) if (b >= 0) => b.toString case _ => "" } }
Scala
Option(4)のwithFilterメソッド、mapメソッドとOption のgetOrElseメソッドの合わせ技で実現できます。
def f(a: Option[Int]): String = { a.withFilter(_ >= 0).map(b.toString) getOrElse "" }
Scalaz
Scalazの場合はOption(7)の技法にOption(4)のwithFilterメソッドまたはfilterメソッドを組合わせて、実現できます。
注意点としては、cataメソッドとfoldメソッドの場合はwithFilterではなくてfilterを使う必要があります。cataメソッドとfoldメソッドはScalazが拡張したメソッドで、ScalaのWithFilterオブジェクトは扱えないからです。
def f(a: Option[Int]): String = { a.withFilter(_ >= 0).map(_.toString) | "" }
def f(a: Option[Int]): String = { a.filter(_ >= 0).cata(_.toString, "") }
def f(a: Option[Int]): String = { a.filter(_ >= 0).fold(_.toString, "") }
def f(a: Option[Int]): String = { a.withFilter(_ >= 0).map(_.toString) orZero }
def f(a: Option[Int]): String = { ~a.withFilter(_ >= 0).map(_.toString) }
ノート
今回はOption(4)のwithFilterメソッドを組み合わせてみましたが、Option(5)のcollectメソッド、Option(6)のflatMapメソッドでも同じように実現できます。
要するに、Option上で(つまり成功失敗の計算文脈上で)計算を続け、最後にcataメソッドなどでOptionから値を取り出すというメカニズムです。
諸元
- Scala 2.9.1
- Scalaz 6.0.3
0 件のコメント:
コメントを投稿