Optionを使う場合にSomeやNoneを作成する処理は当然ながら頻出です。
scala> val a = Some(5) val a = Some(5) a: Some[Int] = Some(5) scala> val b = None val b = None b: None.type = None
通常はこれで問題ないのですが、若干扱いにくい点があります。
上記処理で得られる型にご注目下さい。
「Some(5)」を代入(束縛?)した変数aの型がSome[Int]になっています。さらに、Noneを代入した変数の方がNone.typeになっています。
「Some(5)」や「None」は、型「Option[Int]」の値として扱われる事が多いので、「Some(5)」や「None」を型「Option[Int]」として生成する手段があるとさらに便利になります。
たとえば以下のようなメソッド定義のケースです。この場合、メソッドの返却値の型の定義を省略することができます。
scala> def getFoo = Some(5) def getFoo = Some(5) getFoo: Some[Int] scala> def getBar = None def getBar = None getBar: None.type
またよくあるのは以下のような畳み込み処理の初期値です。畳み込みの初期値の型が「Option[Int]」ではなく「Some[Int]」になっているため、式全体の演算結果としてNoneを返すことができなくなっています。
scala> Vector(1, 2, 3, 4, 5).foldLeft(Some(0))((z, x) => if (x % 2 == 0) None else z.map(_ + x)) se z.map(_ + x)) <console>:14: error: type mismatch; found : None.type required: Some[Int] Vector(1, 2, 3, 4, 5).foldLeft(Some(0))((z, x) => if (x % 2 == 0) None else z.map(_ + x)) ^
Scalaの解
まず、この問題に対するScalaでの対策です。
値がある場合はOption#applyメソッドを使用、値がない場合はOption#emptyメソッドに型「Int」を指定することで型「Option[Int]」を得ることができます。
scala> Option(5) Option(5) res14: Option[Int] = Some(5) scala> Option.empty[Int] Option.empty[Int] res16: Option[Int] = None
前述の例をこの方法で書きなおしたものが以下になります。
scala> def getFoo = Option(5) def getFoo = Option(5) getFoo: Option[Int] scala> def getBar = Option.empty[Int] def getBar = Option.empty[Int] getBar: Option[Int] scala> Vector(1, 2, 3, 4, 5).foldLeft(Option(0))((z, x) => if (x % 2 == 0) None else z.map(_ + x)) else z.map(_ + x)) res17: Option[Int] = None
Scalazの解
Scalazではこの問題に対応するため、任意の値を型「Option[T]」のSome[T]またはNoneに持ち上げる機能を提供しています。
scala> 5.some res30: Option[Int] = Some(5) scala> none[Int] res33: Option[Int] = None
前述の例をこの方法で書き直したものが以下になります。
scala> def getFoo = 5.some getFoo: Option[Int] scala> def getBar = none[Int] getBar: Option[Int] scala> Vector(1, 2, 3, 4, 5).foldLeft(0.some)((z, x) => if (x % 2 == 0) None else z.map(_ + x)) e z.map(_ + x)) res18: Option[Int] = None
個人的な好みの問題もありますがScala版に対して以下の優位点があると思います。
- プログラミング時に書きやすい
- 可読性が高い
具体的には「5.some」の記法は「Option(5)」の記法と比べて以下の優位点があると思います。
プログラミング時の書きやすさ
Scala版では、プログラミング時に「5」と書いた後にSome化したいと思った場合、一度カーソルを前に戻して「Some(」を書いた後、カーソルを文末に移動させて「)」を閉じるという作業が必要になります。それに対して、Scalaz版では「5」と書いた後にSome化したいと思った場合「.some」と書くだけですみます。プログラミングの流れを止めません。
またScala版では「Some(5)」が欲しい時に、場合によっては「Option(5)」にしないといけないので、毎回判断が必要になります。Scalaz版ではどの場合でも「5.some」でOKです。
可読性
Scala版では「Some(5)」の意味で「Option(5)」を使うことがありますが、Optionというオブジェクト名を使用するため、微差ですが意図がわかりづらい面があると思います。
別の切り口として、重要な情報が左側に来る方が可読性が高くなると思います。これを前提にすると、Scala版の「Some(5)」の場合「Some」であることが重要で、「5」の重要度はその次という印象になります。一方、Scalaz版の「5.some」の場合「5」の値が重要で、「some」の重要度はその次という印象になります。多くの場合、「5」の方が重要なので、(これまた微差ですが)「Some(5)」より「5.some」の方が可読性が優れているのではないかと思います。
Scalaz版のまとめ
「プログラミング時の書きやすさ」、「可読性」のいずれも微差なので、好みの方法を使えばよいわけですが、ボクはScalazの提供する「5.some」、「none[Int]」の記法を愛用しています。
諸元
- Scala 2.10.4
- Scalaz 7.0.6
0 件のコメント:
コメントを投稿