関数型言語のモナド (6)
Either は、Maybe を少しだけ拡張したものです。
Maybe モナドと使い方がほぼ同じなので、モナドに慣れるという意味で、ここで Either モナドを扱ってみましょう。
Maybe は失敗した場合、Nothing という情報しか持てませんでした。
しかし、失敗した場合にも何か情報を残したいときがあると思います。その場合に Either を利用します。
まずは、Either の定義を見てみましょう。
data Either a b =
Left a
| Right b
上の定義を見て、以下のことを読み取ってもらえると良いです。
型変数が2つある
値構築子が Left と Right の2つがある。
つまり、Either の値を作りたい場合には、Left または Right を使って構築する。
1. について
成功した場合と、失敗した場合に、それぞれ異なる種類の情報を扱えるようにするためです。
例えば、数値に変換するとき、以下のように利用することができます。
数値に変換できた場合は、変換した数値をコンテナに格納する。
数値に変換できなかった場合は、できなかった理由を文字列としてコンテナに格納する。
上のようにする場合には、数値 または 文字列 を格納できるコンテナが必要になるため、Either String Int 型として利用します。
2. について
失敗した場合 Left コンテナを利用し、成功した場合 Right コンテナを利用するのが一般的な利用法です。 Right という単語には「正しい」という意味があるので、一般的にそのように利用しています。
では、実際に Left と Right を使って、Either の値を作ってみましょう。
今から作る Test4.hs は、Maybe の章で考えたものと同様のものとします。
-- Test4.hs
import Text.Read (readMaybe)
ga str = case (readMaybe str) :: Maybe Int of
Just x -> Right x
Nothing -> Left "not a number"
今回は、readMaybe str の評価値(出力値)が Maybe Int になるように指示 してみました。
Haskell は、1つのことに対していろいろな書き方が用意されていますので、その場その場で読みやすくなるように工夫をしてみてください。
実際に、関数 ga を使ってみましょう。
ghci> :l Test4.hs
ghci> ga "70"
Right 70
ghci> ga "abc"
Left "not a number"
正しく動作していますね。
上の場合、Left a の方には String が格納され、Right b の方には Int が格納されているので、関数 ga の型は、ga :: String -> Either String Int となります。
Either モナド
「Either a b 型に対して利用する場合」の bind 関数 (>>=) は動作は以下のようになります。
(>>=) :: Either a b -> (b -> Either a c) -> Either a c -- この型の意味が分かりにくければ読み飛ばしてください。
(>>=) x f = case x of
Right y -> f y
Left y -> Left y
(>>=) の型はとりあえず無視して、動作を確認してみましょう。
まず、モナドとして 関数のチェーン を考えるので、Either の値(Left または Right の値)が次から次に流れていく ことを想像してください。
第1引数で受け取った x が Right y にマッチする場合、y を f に適用します。評価値 f y は、Left または Right のどちらかの値になります。
第1引数で受け取った x が Left y にマッチする場合、f は無視して、受け取った Left y をそのまま評価値とします。
では、Maybe のときと同じように、60以上 80以下を適正と判断する関数を作ってみましょう。
-- Test4.hs に以下を追加
gb :: Int -> Either String Int
gb x = if x < 60 then Left "too small" else Right x
gc :: Int -> Either String Int
gc x = if x > 80 then Left "too big" else Right (x * 100)
実際にモナドとして利用してみましょう。
ghci> :l Test4.hs
ghci> ga "70"
Right 70
ghci> it >>= gb
Right 70
ghci> it >>= gc
Right 7000
ghci> ga "abc"
Left "not a number"
ghci> it >>= gb
Left "not a number"
ghci> it >>= gc
Left "not a number"
Maybe のときに試してみたように、関数のチェーンを h にまとめて利用してみましょう。
ghci> h str = ga str >>= gb >>= gc
ghci> h "70"
Right 7000
ghci> h "abc"
Left "not a number"
ghci> h "50"
Left "too small"
ghci> h "90"
Left "too big"
この記事が気に入ったらサポートをしてみませんか?