2012年2月3日金曜日

Scala Tips / Option (7)

Optionから値を取り出すイディオムです。

ここまで以下の演算と:

条件結果
Option[A]がSome[A]Some[B]
Option[A]がNoneNone

以下の演算についてみてきました。

条件結果
Option[A]に有効な値が入っているSome[B]
Option[A]に無効な値が入っているNone
Option[A]がNoneNone

前者は、成功の文脈と失敗の文脈の切り替えが発生しない演算、後者は、成功の文脈と失敗の文脈の切り替えが発生する演算です。

今回はこの2つの演算をfor式を使って書いてみます。

Some[A]→Some[B]

Option(3)で取り上げた成功の文脈と失敗の文脈の切り替えが発生しない演算です。以下の表に示す演算になります。

条件結果
Option[A]がSome[A]Some[B]
Option[A]がNoneNone

Option[Int]からOption[String]へ変換は以下のようになります。

def f(a: Option[Int]): Option[String] = {
  for (b <- a) yield b.toString
}

Option(3)で使用したmapメソッドと同様に、Option[A]がNoneだった場合の処理を書く必要がありません。

Some[A]→None

次はOption(4)で取り上げた成功の文脈と失敗の文脈の切り替えが発生する演算です。以下の表に示す演算になります。

条件結果
Option[A]に有効な値が入っているSome[B]
Option[A]に無効な値が入っているNone
Option[A]がNoneNone

Intは0以上のものが有効という条件付きのOption[Int]からOption[String]へ変換は以下のようになります。

def f(a: Option[Int]): Option[String] = {
  for (b <- a if b >= 0) yield b.toString
}

for式内のif句で、Some[A]をNoneに切り替える条件を指定することができます。

ノート

Javaのfor文は構造化プログラミングにおける繰り返しを記述するための文でしたが、Scalaのfor式はモナドによる演算の文法糖衣となっています。

たとえば、以下のプログラムは、一見普通のfor文に見えますが、実際は0オブジェクト(Int型)のuntilメソッドが返すRangeオブジェクトがモナドで、このRangeオブジェクトに対するモナドの演算を行っています。

for (i <- 0 until 10) {
  println(i)
}

モナドに対する演算を伝統的なfor文に見せているのがScalaの芸の細かいところで、Java言語などから移行する場合の敷居を低くしています。普通にプログラミングしていても自然にMonadicプログラミングをしているということになるわけで、使い方に慣れたところで徐々にMonadicプログラミング的な作法を取り入れていけばよいようになっています。

for式には、上記の(yield句なしの)for式と本文中で用いているyield句ありのfor式の2種類があります。

yield句ありのfor式の場合は、内部的に今までOption操作で使ってきた、mapメソッド、flatMapメソッド、withFilterメソッドを使って処理を行っています。つまり、完全な文法糖衣ということです。(yield句なしのfor式ではforeachメソッドを使います。)

今回取り上げているOptionから値を取り出すといった小さな処理では、mapメソッドなどを直接使ってもfor式を使っても効果はそれほど変わりません。好みの方を使うとよいでしょう。

ただし、Option(6)のようにflatMapメソッドを使って細かい操作をしたい場合や、Option(5)のようにcollectメソッドのようなfor式がカバーしていないメソッドを使いたい場合は、for式は諦めることになります。

逆に本格的なMonadicプログラミングを行う場合、for式を使うと見通しがよくなるケースがあるので、こういう場合はfor式を積極的に使っていきたいところです。

このあたりの選択のポイントについてもテーマとして考えていきたいと思います。

諸元

  • Scala 2.9.1
  • Scalaz 6.0.3

0 件のコメント:

コメントを投稿