Haskellのモナドいろいろ (1)

Listモナド

リストもモナドである。意外なことにリストもモナドである。
リストには要素数が0個の空リストがありうる。
これは後述するMaybe や Either における失敗の表現のようなものとも言える。

モナドだから return で値を中に入れることができる。

hoge::Int -> [Int]
hoge x = return x

main = do
    print $ hoge 5 -- [5]

モナドだから join で一皮むくことができるが、 join で裸にはできない。

import Control.Monad

main = do
    let a = [[1,2],[3,5],[5,6]]
    let b = [[[1],[2]],[[3],[4]]]
    let c = [1]
    print $ join a -- [1,2,3,5,5,6]
    print $ join b -- [[1],[2],[3],[4]]
--  print $ join c -- エラー

モナドだから fmap で中の値を関数に渡すことができる。
リストには特に map というのも用意されているが、結果は fmap と同じ。

main = do
    let a = [1,2,3,4,5]
    print $ fmap (*2) a -- [2,4,6,8,10]
    print $ map  (*2) a -- [2,4,6,8,10]

モナドだから bind ( >>= ) で中の値を、モナドを返す関数に渡すことができる。

main = do
    print $ [7] >>= replicate 3 -- [7,7,7]

モナドだから do構文が使える。

hoge :: [Int] -> [Int]
hoge xs = do
    x <- xs
    return $ x * 2

main = do
    print $ hoge [1,2,3,4,5] -- [2,4,6,8,10]

Maybeモナド

Maybeは成否を表現できる。成功なら Just 値、失敗なら Nothing。
1個か0個しか要素を持てないリストのようなものとも言える。

モナドだから return で値を中に入れることができる。

hoge::Int -> Maybe Int
hoge x = return x

main = do
    print $ hoge 5 -- Just 5

モナドだから join で一皮むくことができるが、 join で裸にはできない。

import Control.Monad

main = do
    let a = Just(Just 5) :: Maybe (Maybe Int)
    let b = Just Nothing :: Maybe (Maybe Int)
    let c = Just 2 
    print $ join a -- Just 1
    print $ join b -- Nothing
--  print $ join c -- エラー

モナドだから fmap で中の値を関数に渡すことができる。

main = do
    let a = Just 5
    print $ fmap (*2) a -- Just 10

モナドだから bind ( >>= ) で中の値を、モナドを返す関数に渡すことができる。

hoge :: Int -> Maybe Int
hoge x
    | x >= 10   = Just (x `div` 10)
    | otherwise = Nothing

main = do
    print $ Just 50 >>= hoge -- Just 5
    print $ Just 5  >>= hoge -- Nothing

モナドだから do構文が使える。

hoge :: Maybe Int -> Maybe Int
hoge mx = do
    x <- mx
    if x >= 10
        then return (x `div` 10)
        else Nothing

main = do
    print $ hoge (Just 50) -- Just 5
    print $ hoge (Just 5)  -- Nothing
    print $ hoge Nothing   -- Nothing

失敗するかもしれない関数を連鎖できる。
途中で失敗した場合、その後の計算は行われず最終結果は失敗になる。

hoge :: Int -> Maybe Int
hoge x
    | x >= 10   = Just (x `div` 10)
    | otherwise = Nothing

main = do
    print $ Just 500 >>= hoge >>= hoge -- Just 5
    print $ Just 50  >>= hoge >>= hoge -- Nothing
    print $ Just 5   >>= hoge >>= hoge -- Nothing

Eitherモナド

Eitherは成否を表現でき、失敗にも値(エラー情報)を持てる。成功なら Right 値、失敗なら Left 値。
Maybeの拡張してエラー情報を持てるようにしたようなものと言える。

モナドだから return で値を中に入れることができる。

hoge::Int -> Either String Int 
hoge x = return x

main = do
    print $ hoge 5 -- Right 5

モナドだから join で一皮むくことができるが、 join で裸にはできない。

import Control.Monad

main = do
    let a = Right(Right 5)     :: Either String (Either String Int) 
    let b = Right(Left "hoge") :: Either String (Either String Int) 
    let c = Right 2            :: Either String Int
    print $ join a -- Right 5
    print $ join b -- Left "hoge"
--  print $ join c -- エラー

モナドだから fmap で中の値を関数に渡すことができる。

main = do
    let a = Right 5 :: Either String Int
    print $ fmap (*2) a -- Right 10

モナドだから bind ( >>= ) で中の値を、モナドを返す関数に渡すことができる。

hoge :: Int -> Either String Int
hoge x
    | x >= 10   = Right (x `div` 10)
    | otherwise = Left "error"

main = do
    print $ Right 50 >>= hoge -- Right 5
    print $ Right 5  >>= hoge -- Left "error"

モナドだから do構文が使える。

hoge :: Either String Int -> Either String Int
hoge mx = do
    x <- mx
    if x >= 10
        then return (x `div` 10)
        else Left "error"

main = do
    print $ hoge (Right 50)    -- Right 5
    print $ hoge (Right 5)     -- Left "error"
    print $ hoge (Left "hoge") -- Left "hoge"

失敗するかもしれない関数を連鎖できる。
途中で失敗した場合、その後の計算は行われず最終結果は失敗になる。

hoge :: Int -> Either String Int
hoge x
    | x >= 10   = Right (x `div` 10)
    | otherwise = Left "error"

main = do
    print $ Right 500 >>= hoge >>= hoge -- Right 5
    print $ Right 50  >>= hoge >>= hoge -- Left "error"
    print $ Right 5   >>= hoge >>= hoge -- Left "error"