見出し画像

関数型言語のモナド (6)

Either は、Maybe を少しだけ拡張したものです。
Maybe モナドと使い方がほぼ同じなので、モナドに慣れるという意味で、ここで Either モナドを扱ってみましょう。

Maybe は失敗した場合、Nothing という情報しか持てませんでした。
しかし、失敗した場合にも何か情報を残したいときがあると思います。その場合に Either を利用します。

まずは、Either の定義を見てみましょう。

data Either a b =
    Left a
    | Right b

上の定義を見て、以下のことを読み取ってもらえると良いです。

  1. 型変数が2つある

  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.  第1引数で受け取った xRight y にマッチする場合、y を f に適用します。評価値 f y は、Left または Right のどちらかの値になります。

  2.  第1引数で受け取った xLeft 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"


この記事が気に入ったらサポートをしてみませんか?