Haskell Maybeモナド 超入門の解答例です。
フィボナッチ数
【問1】次の関数fibでエラーを回避するためMaybeモナドで書き換えてください。
import Control.Applicative
fib 0 = Just 0
fib 1 = Just 1
fib n | n > 1     = (+) <$> fib (n - 2) <*> fib (n - 1)
      | otherwise = Nothing
main = do
    print $ fib (-1)
    print $ fib 6
実行結果
Nothing
Just 8
再実装
bind
【問2】Maybeモナドを扱うbindを実装してください。
(Just a) `bind` b = b a
Nothing  `bind` _ = Nothing
main = do
    print $ Just 1 `bind` \a -> Just $ a * 2
    print $ Just 1 `bind` \a -> Nothing `bind` \b -> Just $ a * b
実行結果
Just 2
Nothing
mapMaybe
再帰
【問3】mapMaybeを再帰で再実装して、先ほどのサンプルで検証してください。
import Control.Applicative
mapMaybe _ []     = []
mapMaybe f (x:xs) = case f x of
    Just y  -> y : mapMaybe f xs
    Nothing ->     mapMaybe f xs
fact 0 = Just 1
fact n | n > 0     = (n *) <$> fact (n - 1)
       | otherwise = Nothing
facts n = ( map      fact [n, n - 1, n - 2]
          , mapMaybe fact [n, n - 1, n - 2]
          )
main = do
    print $ facts 3
    print $ facts 2
    print $ facts 1  -- cで失敗
    print $ facts 0  -- bで失敗
実行結果
([Just 6,Just 2,Just 1],[6,2,1])
([Just 2,Just 1,Just 1],[2,1,1])
([Just 1,Just 1,Nothing],[1,1])
([Just 1,Nothing,Nothing],[1])
foldr
【問4】問3の解答をfoldrで書き換えてください。
import Control.Applicative
mapMaybe f = (`foldr` []) $ \x xs -> case f x of
    Just x' -> x':xs
    Nothing ->    xs
fact 0 = Just 1
fact n | n > 0     = (n *) <$> fact (n - 1)
       | otherwise = Nothing
facts n = ( map      fact [n, n - 1, n - 2]
          , mapMaybe fact [n, n - 1, n - 2]
          )
main = do
    print $ facts 3
    print $ facts 2
    print $ facts 1  -- cで失敗
    print $ facts 0  -- bで失敗
実行結果
([Just 6,Just 2,Just 1],[6,2,1])
([Just 2,Just 1,Just 1],[2,1,1])
([Just 1,Just 1,Nothing],[1,1])
([Just 1,Nothing,Nothing],[1])
文字列チェック
【問5】文字列sが「x文字の連続した数字+y文字の連続した大文字」で構成されるか判定する関数numUpper x y sを実装してください。成功した場合はJust s、失敗した場合はNothingを返してください。余計な文字は含まないものとします。
import Data.Char
import Control.Monad
numUpper x y s = do
    guard $ length s == x + y
    guard $ length (filter isDigit $ take x s) == x
    guard $ length (filter isUpper $ drop x s) == y
    Just s
main = do
    print $ numUpper 3 2 "123AB"
    print $ numUpper 3 2 "123ABC"
    print $ numUpper 3 2 "12ABC"
実行結果
Just "123AB"
Nothing
Nothing
Alternative
【問6】文字列sの先頭3文字が「数字+大文字+小文字」または「大文字+小文字+小文字」で構成されるかを判定する関数check sを実装してください。<|>を使って、成功した場合はJust s、失敗した場合はNothingを返してください。後続の文字は判定に影響しないものとします。
import Data.Char
import Control.Applicative
import Control.Monad
check s = do
    guard $ length s >= 3
    do
        guard $ isDigit $ s !! 0
        guard $ isUpper $ s !! 1
        <|> do
        guard $ isUpper $ s !! 0
        guard $ isLower $ s !! 1
    guard $ isLower $ s !! 2
    Just s
main = do
    print $ check "1"
    print $ check "2Ab"
    print $ check "Abc"
    print $ check "Ab1"
    print $ check "1AB"
実行結果
Nothing
Just "2Ab"
Just "Abc"
Nothing
Nothing