2014年5月26日月曜日

かんたんScalaz/Option編

かんたんScalazのOption編です。

Scalaプログラミングでは、Optionはキーとなる重要なオブジェクトで、Optionの取り回し方の優劣がプログラミングの精度・効率に大きく作用します。

ScalazでもOption向けに多くの機能を用意しています。

「かんたんScalaz」ということでScalazが提供しているMonadやMonoidといった難しい概念を抜きにして便利に使えるOptionの機能をまとめてみました。

準備

説明の準備に以下の関数を定義します。

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]

?と|

OptionをBoolean型に見立てて値を決めたい場合に、3項演算子的な書き方ができると便利です。Scalazでは「?」と「|」の組合せでこれが実現できます。

scala> a ? "defined" | "undefined"
a ? "defined" | "undefined"
res12: String = defined

scala> b ? "defined" | "undefined"
b ? "defined" | "undefined"
res13: String = undefined

Scala基本機能のみでもif式で以下のように書ける上に実行性能も圧倒的にこちらの方が速いので、通常はこれでも十分ですが、上記の方が短く書けるので好みによって使ってみるのもよいと思います。

scala> if (a.isDefined) "defined" else "undefined"
if (a.isDefined) "defined" else "undefined"
res44: String = defined

個人的には「?」と「|」の方が可読性が高いように思うので、性能が気にならないところ(例:フレームワークのコア機能以外)ではよく使っています。

OptionがSomeだった場合は格納された値、そうでなかった場合はデフォルト値を取得する処理はよく出てきます。

通常は、OptionのgetOrElseメソッドを使いますが、Scalazでは「|」でこれを記述することができます。

scala> a | 10
a | 10
res17: Int = 100

scala> b | 10
b | 10
res18: Int = 10

個人的には「|」の方が可読性が高いように思うので、性能が気にならないところ(例:フレームワークのコア機能以外)ではよく使っています。

some/none

OptionがSomeだった場合は格納された値に演算を施した値、そうでなかった場合はデフォルト値を取得する処理もよく出てきます。

Scalaの基本機能で記述する場合、以下のようになると思います。

scala> a match {
  case Some(x) => x + 100
  case None => 0
}
res45: Int = 200

scala> a.map(_ + 100).getOrElse(0)
a.map(_ + 100).getOrElse(0)
res46: Int = 200

どちらの方法でもよいですが、頻出処理なのでもっと簡略化した記法が使えるとうれしいところです。

Scalazではこの目的で「some」と「none」の組合せでの記述方法を用意しています。

scala> a some(_ + 100) none(0)
a some(_ + 100) none(0)
res15: Int = 200

scala> b some(_ + 100) none(0)
b some(_ + 100) none(0)
res16: Int = 0

また前述の「|」を使用した以下の記述方法も便利です。

scala> a.map(_ + 100) | 0
a.map(_ + 100) | 0
res47: Int = 200

orZero

orZeroメソッドは「かんたんScalaz/Boolean編」で紹介した「??」メソッドや「!?」メソッドで用いているMonoidの性質を利用したメソッドです。

OptionがSomeの場合は格納された値を返しますが、そうでない場合は格納された値のMonoidとしての単位元を返します。このためMonoidである型にしか適用できませんが、基本データ型やコレクションなどはほとんどMonoidなのでかなり適用範囲は広いです。

scala> a.orZero
a.orZero
res22: Int = 100

scala> b.orZero
b.orZero
res23: Int = 0

上記プログラムの動きは以下のようになります。

OptionがSomeの場合は、Someに格納されている値である100が返ります。一方Noneの場合は、以下の動きになっています。

  • Optionに格納されている型はInt
  • Intの単位元は「0」
  • OptionがNoneなのでIntの単位元である「0」を返す

Monoidというと敷居が高いので、「かんたんScalaz」の文脈では型ごとに初期値を持っている、ぐらいの捉え方でよいと思います。この「初期値」は、数値系なら「0」、ListなどのコレクションはNilなどの「空」となりますので、0や空といった値になると覚えておいて、実際の値は必要に応じて調べるというアプローチでよいでしょう。

「〜」を使う以下の書き方も用意されていますが、見落としや誤読してしまいそうなのでボクは使わないようにしています。

scala> ~a
res48: Int = 100

scala> ~b
res49: Int = 0

参考

Option Index

Optionに関する2012年ごろのブログのまとめです。

Optionに関しては、この当時とそれほど見方は変わっていません。このため、このあたりの記事も現役として参考にしていただけると思います。

ただ、プログラミングしている中で色々なバランスが見えてきた部分もあると思うので、棚卸しをした上で適宜追加情報や新しいバランス上での使い方についてまとめていきたいと思います。

諸元

  • Scala 2.10.4
  • Scalaz 7.0.6

0 件のコメント:

コメントを投稿