エラーが起こる関数
次の関数 fb は負数を与えるとエラーになる。
-- フィボナッチ数列 fib:: Int -> Int fib 0 = 0 fib 1 = 1 fib n | n > 1 = fib (n - 2) + fib (n - 1) main = do print $ fib 20 -- 6765 print $ fib (-1) -- エラー
そこでMaybeモナド
Maybeモナドを使うとエラーを戻り値 Nothing として扱える。
-- フィボナッチ数列 fib:: Int -> Maybe Int fib 0 = Just 0 fib 1 = Just 1 fib n | n > 1 = do a <- fib (n - 2) b <- fib (n - 1) Just (a + b) fib n | n < 0 = Nothing main = do print $ fib 20 -- Just 6765 print $ fib (-1) -- Nothing
Just は return と書いても同じ。
-- フィボナッチ数列 fib:: Int -> Maybe Int fib 0 = return 0 fib 1 = return 1 fib n | n > 1 = do a <- fib (n - 2) b <- fib (n - 1) return (a + b) fib n | n < 0 = Nothing
ファンクタ<$>やアプリカティブファンクタ<*>を用いて次のようにも書ける
-- フィボナッチ数列 fib:: Int -> Maybe Int fib 0 = return 0 fib 1 = return 1 fib n | n > 1 = (+) <$> fib (n - 2) <*> fib (n - 1) fib n | n < 0 = Nothing
fromMaybe関数
fromMaybe関数を使うと、Maybeモナドから値を取り出せる。
第一引数にはデフォルト値(Nothingだったときの値)を渡す。
import Data.Maybe -- フィボナッチ数列 fib:: Int -> Maybe Int fib 0 = return 0 fib 1 = return 1 fib n | n > 1 = (+) <$> fib (n - 2) <*> fib (n - 1) fib n | n < 0 = Nothing main = do print $ fromMaybe 0 (fib 20) -- 6765 print $ fromMaybe 0 (fib (-1)) -- 0
bind (=<<)
Maybeモナドを返す関数にMaybeモナドを渡すには bind (=<<) を用いる。
-- フィボナッチ数列 fib:: Int -> Maybe Int fib 0 = return 0 fib 1 = return 1 fib n | n > 1 = (+) <$> fib (n - 2) <*> fib (n - 1) fib n | n < 0 = Nothing -- 1から9の英語 english:: Int -> Maybe String english n | (n >= 1) && (n <= 9) = return s where words = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] s = words !! (n-1) english _ = Nothing main = do print $ english =<< fib 6 -- Just "eight" print $ english =<< fib 20 -- Nothing print $ english =<< fib (-1) -- Nothing
ちなみに、この関数 english は次のようにも書ける。
english:: Int -> Maybe String english n = do let words = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] if (n >= 1) && (n <= 9) then return (words !! (n-1)) else Nothing
english:: Int -> Maybe String english n = do let words = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] when (n < 1) Nothing when (n > 9) Nothing return (words !! (n-1))
ファンクタ(<$>) と アプリカティブ(<*>) と バインド (=<<) の違い
<$>:モナドから値を取り出して、ふつうの関数に渡し、戻り値を(同種の)モナドに入れる。
<*>:モナドから値を取り出して、(同種の)モナドに入った関数に渡す。戻り値は(同種の)モナドに入る。
=<<:モナドから値を取り出して、(同種の)モナドを返す関数に渡す。
huga:: Int -> Int huga n = n * n hoge:: Maybe (Int -> Int) hoge = Just (\x -> x * x) piyo:: Int -> Maybe Int piyo n = Just (n * n) main = do print $ huga <$> Just 3 -- Just 9 print $ hoge <*> Just 3 -- Just 9 print $ piyo =<< Just 3 -- Just 9
また、ファンクタは fmap関数で書いても同じ。
print $ huga <$> Just 3 -- Just 9 print $ fmap huga (Just 3) -- Just 9