人気ブログランキング | 話題のタグを見る

Maybe モナド

Haskell を敬遠する理由の一つがモナドだ。圏論の用語で、圏論の本で定義を見ても何のことかさっぱりわからない。しかし、使う側から考えると、モナドの本当の意味は分からなくても、データ型の一つで、例外処理もできるすぐれものだくらいの意識で使えるような気がする。

説明より実際に使ってみたほうが早い。モナド型のデータを戻り値にする関数に lookup がある。lookup は ("a", "b") の2値のタプル(値の組)のリスト [("a","b"), ("c","d"), ("e","f")]から、タプルの第一要素をキーとして第2要素を取り出す関数だ。Perl や Ruby の連想配列のようなものだ。

Hugs> lookup "a" [("a","b"), ("b", "c"), ("d", "e")]
Just "b"
Hugs> lookup "f" [("a","b"), ("b", "c"), ("d", "e")]
Nothing

このときの戻り値の型が Maybe モナドだ。検索が成功したときは Just 値が戻されそのフィールドに検索した第2要素の値が収められている。検索が失敗したときはNothing値が返される。このような複数の種類の値が帰る時は、一般にはその値を次の関数に渡す前にロジックが必要になってくる。ところが、Maybe モナドの場合 >>= 演算子を使うとロジックなしに次の関数に値を渡すことができる。

例を挙げてみよう。上にあげたタプルのリスト以外に、階層構造をもったデータも考えられる。たとえば、[("a", [("b", "c")])] のようなデータの場合タプルのリストの第二引数が、再びタプルのリストになっている。このような場合下の例のように Just の値がタプルのリストになる。

Hugs> lookup "a" [("a", [("b", "c")])]
Just [("b","c")]

そこで、今度は戻り値のなかのキー "b" に対応する値 "c" を求めたい場合がある。これも一般の言語なら、if lookup("a", lst) != nothing then lookup("b", lookup("a", lst)) のようなロジックになるだろう。ところが、Maybe モナドを使うとこれが >>= 演算子ひとつで実行されてしまう。たとえば、上の例でタプル("b", "c") の第二要素 "c" の値を得たいときのプログラムは次のようになる。

Hugs> lookup "a" [("a", [("b", "c")])] >>= lookup "b"
Just "c"

また、検索でキーのマッチが成功しなかった場合はどのレベルで不成功でも、全体の検索が不成功だったとしてNothingが返されるだけで、エラーのためにプログラムが停止するということもない。

Hugs> lookup "d" [("a", [("b", "c")])] >>= lookup "b"
Nothing
Hugs> lookup "a" [("a", [("b", "c")])] >>= lookup "e"
Nothing

理屈や仕組みはひどく難しいモナドだが、例外をうまく処理してくれる便利なデータ型だと割り切ってしまうと使いやすく感じる。

Haskell は理屈より使ってみることから学んだほうが分かりやすいようだ。今までの言語とは異なる感覚が必要だが、むしろ、簡潔で可読性の高いプログラムが作れるような気がする。

注: Maybe 型の値から、Just の値を取り出すには、fromJust や、maybeToList 関数を使う。

Hugs> :load Maybe
Maybe> fromJust $ Just 1
1
Maybe> maybeToList $ Just 1
[1]

また、Maybe 関連の関数のリストを取るには、:load Mayge, :browse Maybe をすると良いようだ。

Hugs> :load Maybe
Maybe> :browse Maybe
module Maybe where
isJust :: Maybe a -> Bool
isNothing :: Maybe a -> Bool
fromJust :: Maybe a -> a
fromMaybe :: a -> Maybe a -> a
listToMaybe :: [a] -> Maybe a
maybeToList :: Maybe a -> [a]
catMaybes :: [Maybe a] -> [a]
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
Nothing :: Maybe a -- data constructor
Just :: a -> Maybe a -- data constructor
maybe :: a -> (b -> a) -> Maybe b -> a
by tnomura9 | 2009-08-08 11:13 | Haskell | Comments(0)
<< WinHugs の使い方 Haskell の遅延評価 >>