Haskell の Maybe a 型と Either a b 型 (1) の続き。
Maybe a 型と Either a b 型の類似性
Maybe a 型のコンストラクタは、
Nothing
この型の意味は 前回 見たように「何もないよの `Nothing’」 と「何かあったよ `Just a’」 で、関数によって取り出したい値が ある場合 と ない場合 を総称した概念を表現している。例えば、何かを検索するための関数の場合、値を見つけたときに Just で包み、見つからなかったら Nothing を返す。他の言語で null や None を返すような曖昧なことをするのではなく、「あった場合、なかった場合」を一つの型としてコンパイラがキッチリとチェックする。
この Maybe a 型と似ているのが Either a b 型。
either の言葉としての日常的な使い方は、
Either you or she is to go.
君か彼女かどちらかが行かねばならない(Yahoo!辞書 – either より)
文字通り「どちらか一方」を表わす型。
「どちらか一方」という意味では、Maybe a 型の Nothing と Just も「あるかないか」のどちらか一方を返す関数で使われる。
Either a b 型のコンストラクタを見ると、Maybe によく似ている。
Maybe と対応させて解釈すると都合がいい。
ただし、各々を対応させたのは便宜的なもので、関数を適用した結果、二つの内どちらかに分類することがその本質。違いは、Maybe a 型が Nothing を返したら「もう後はおらぁ知らねぇ~」なのに対して、対応する Either a b 型は律儀にも Left で値を包んで返してくれる。
Maybe a 型と Either a b 型の違い
Maybe a 型の例
例えば、次のような状況を想定。
「合格ラインが 80 点のテスト」がある。受験生は合格ラインの得点を知らない。
これに対して、合否を判定する test 関数を定義し、 Maybe a 型を利用してみる。合格なら Just、不合格なら Nothing を返すとする。
goukaku = 80 test score = if score >= goukaku then Just "Goukaku!" else Nothing
例えば、このテストで 80点、79点をとったとして test 関数を適用すると、
*Main> test 80 Just "Goukaku!" *Main> test 79 Nothing
79点の人は問答無用の Nothing で門前払い。自分が何点合格まで足りなかったのかわからない。
Either a b 型の例
これでは受験生も納得できないだろうと考え、
「合格得点まで何点足りなかったのか?」
を返すように test 関数を変更することにした。
今度は Either a b 型を使い、合格を Right、不合格を Left で表現するとして test’ 関数を定義。
test' score = if score >= goukaku then Right "Goukaku!" else Left $ "Ato " ++ show (goukaku - score) ++ " ten tari nai"
先ほどと同じように、80点、79点をとったとして test’ 関数を適用すると
*Main> test' 80 Right "Goukaku!" *Main> test' 79 Left "Ato 1 ten tari nai"
上記のように Maybe は Just で値を包まない限り Nothing が返され、それ以上何も伝えることができない。それに対して、Either では Maybe の Nothing に相当する Left でも何かを伝えることができる。
maybe と either 関数 の類似性
とは言ったものの、最初は Maybe a 型と Either a b 型がなぜ Prelude の Basic data types にあるのか全く見当がつかなかった。 (+_+)
Maybe によると、
Using Maybe is a good way to deal with errors or exceptional cases without resorting to drastic measures such as error.
これは 前回見た elemIndex 関数 のように、Maybe a 型は見つからなかったときに error で実行を止めてしまうのではなく、「なかった!」ということをこの関数の呼出し元に伝えるための方法であり、そのための型であるということ。
続いて Maybe の説明には次のように書かれている。
It is a simple kind of error monad, where all errors are represented by Nothing. A richer error monad can be built using the Data.Either.Either type.
(同上より)
この辺りで「ん?」 (@_@) … と。
Error モナドについては横に置いておくとして、エラーを伝える方法として Either は Maybe の親戚のようなものだと認識。
Data.Maybe, Data.Either から import されている関数
ところで、Maybe, Either は、それぞれ Data.Maybe, Data.Either で定義されており、いくつか関数が定義されている。しかし、Prelude には各々一つの関数しか import されていない。それが以下の二つ。
最初、引数がごちゃごちゃしていてやだなぁ (@_@;) と感じた。しかし、説明を読めば、両者とも
第3引数 の値に応じて、第1引数 もしくは 第2引数 が評価される関数
であることがわかる。
図示すれば、以下のように maybe と either の引数の間に対応関係がある。
そして、第3引数の値が与えられると、返ってきた値に応じて第1引数、第2引数の式が評価される。
either 関数に関しては、返ってきた値によって、文字通り Left または Rihgt にある関数が評価されるのでわかりやすい。
ちなみに、If-then-else – HaskellWiki には if 式を関数に置き換えている例があるが、上記はこれに似ている。ただし、こちらは第1引数の値に応じて、第2第3引数が評価される。
if' :: Bool -> a -> a -> a if' True x _ = x if' False _ y = y
… ということは、maybe, either 関数は、条件分岐の Maybe a, Either a b 型版と言ったところか。
追記 (2009.11.15) : Data.Bool.HT パッケージに if’ が定義されている。(via Case – HaskellWiki )
試してみる
例えば、先ほどの test 関数の結果を第3引数に渡し、
「試験に合格した場合」と「不合格な場合」にかける言葉を返す hyouka 関数
を定義してみる。
hyouka = maybe "Zannen (;_;" (++ " omedetou! (^^") . test
例えば、
*Main> hyouka 80 "Goukaku! omedetou! (^^" *Main> hyouka 79 "Zannen (;_;"
either 関数も同様に、
hyouka' = either (++ "nante zannen (+_+") (++ " omedetou! (^^") . test'
例えば、
"Goukaku! omedetou! (^^" *Main> hyouka' 79 "Ato 1 ten tari nainante zannen (+_+"
0コメント:
コメントを投稿