無が存在するという中二な問題。
注意!
凄く初歩的な話しなので下の方に書いてあるコードを見て「あー それのことね」って思う人は読む必要がないです。
注意!おわり
Scalaのお話の前に。
プログラムを書いていて、何か値を取得したい場面があったとする。
その値は次の3つの状態をとる。
- 無以外の値
- 無という値
- 値が存在しない
1の”無以外の値”は問題ないのだが2と3を区別するのに非常に苦労する。
例えば辞書をひく場合を考えてみよう。
ある単語の意味を調べる為に辞書を引き、その意味を受け取るプログラムを作る。
上の3つの状態はある単語の意味に対して以下のように対応する。
- 単語が辞書に在り、意味が載っている
- 単語が辞書に在り、「意味が存在しない」という意味が書かれている。
- 単語が辞書に載っていない(意味が存在しない)。
かなり意地悪な例かもしれないが、プログラミングをするとよく出会う場面だと思う。
しかも、あるデータ集合に存在する例外的な値の存在で条件分岐が複雑になったり、またはその例外的な値の存在のチェックを忘れて厄介なバグを生み出してしまう。
例えばJavaの場合だと以下の3つのような場面
- null以外の値がある
- nullという値がある
- 値がない
結局3の値が無いも2と同じnullで表す場合があり、nullが2つの意味を持ってしまっていて非常に怖い。
nullでエラーメッセージを出すにしても「値はあるけどnullが設定されていますよ!」っていうエラーメッセージを出せばいいのか「値がないぞ!」ってエラーメッセージを出せばいいのか分からない。
そして結局のところnullチェックを忘れてぬるぽで死ぬ。
前置きが長くなったが、Scalaを使えばこの問題は全て解決する。
ScalaにはOptionというクラスが有り、そのサブクラスにSomeとNoneが在る。
Someは何らかの値が存在することを表すクラスで、Some("hoge")の様に何らかの値を包んで渡す。
Noneは値が存在しないことを示すクラスでそれ単体で使用する。
Javaの例で出した3つの状態をOptionを使って表せば以下のようになる。
- Some("hoge") //null以外の値がある
- Some(null) //nullという値がある
- None //値がない
辞書の例だと以下のようになる
- Some("何かの意味")
- Some("意味が存在しない")
- None //意味が存在しない(辞書に載ってない)
Scalaのコードで書くと以下のようになる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val list = List(Some("hoge"), Some(null), None) | |
list.foreach{ | |
case Some(null) => println("nullという値がある.") | |
case Some(a) => println(a + "という値がある.") | |
case None => println("値が無い.") | |
} | |
// 結果. | |
// hogeという値がある. | |
// nullという値がある. | |
// 値が無い. |
Optionの細かい説明は省くが以下の様な利点があって非常に簡潔に書ける。
パターンマッチを行うことでif文がネストすること無く幾つもの状態を切り分けることが出来る。
またSome(a)と書くことによりSomeの中の値を変数aに束縛出来るので値を取り出す処理を記述する必要もない。
更にパターンマッチにNoneの記述忘れがあるとコンパイラが警告を出すので条件の記述漏れがなくなる!!! ← すごく重要。JavaやC等の静的型付け言語ではifでのnullチェック漏れなどは警告してくれません(よく言われる”ぬるぽ”が発生する原因がここにあります)。
非常に詳しく分かりやすく書かれています。
私の尊敬する方です。ありがとうございます。
私の尊敬する方です。ありがとうございます。
0 件のコメント:
コメントを投稿