top page > computer > haskell > web_lecture > for_programmer > monad_comprehensions.html
更新日:
文責: 重城良国

拡張機能: MonadComprehensions

(工事中 50%)

はじめに

リスト内包表記はリストモナドをすっきりとわかりやすく表現できる。リスト内包表記は条件式以外ではリストのモナドとしてのインターフェースしか使っていない。よってリスト内包表記はより一般的なモナドへと拡張できる。それをモナド内包表記と呼ぶ。

言語拡張

モナド内包表記はHaskellの標準的な機能ではない。GHCの言語拡張として使える。ソースファイルの先頭に

{-# LANGUAGE MonadComprehensions #-}

と書く。

Maybe内包表記

monad_comprehensions.hs

安全なわり算を定義する。

safeDiv :: Int -> Int -> Maybe Int
_ `safeDiv` 0 = Nothing
x `safeDiv` y = Just $ x `div` y

a / b / cを安全に計算する関数をまずはdo記法で書く。モナド内包表記に変換するために冗長なreturnを使っている。

calc :: Int -> Int -> Int -> Maybe Int
calc a b c = do
x <- a `safeDiv` b
y <- x `safeDiv` c
return y

これをモナド内包表記に変換してみよう。

calc = [ y | x <- a `safeDiv` b, y <- x `safeDiv` c ]

Calc内包表記

ソースコードの先頭に

{-# LANGUAGE MonadComprehensions, TupleSections #-}

を書く。Calc型を作成し型クラスFunctor, Applicative, Monadのインスタンスにする。

newtype Calc a = Calc { runCalc :: Int -> (a, Int) }

instance Functor Calc where
fmap = (=<<) . (return .)

instance Applicative Calc where
pure = return
mf <*> mx = do
f <- mf
x <- mx
return $ f x

instance Monad Calc where
return = Calc . (,)
m >>= f = Calc $ \s ->
let (x, s') = runCalc m s in runCalc (f x) s'

メモリへの加算、メモリの内容の呼び出し関数を定義する。

mplus :: Int -> Calc ()
mplus x = Calc $ (() ,) . (+ x)

mrecall :: Calc Int
mrecall = Calc $ \s -> (s, s)

(3 * 4 + 2 * 5) * 7をまずはdo記法で書いてみる。モナド内包表記に書きなおすことを考えて最後をreturnで終わるようにしておく。

calcC :: Calc Int
calcC = do
mplus $ 3 * 4
mplus $ 2 * 5
x <- mrecall
return $ x * 7

モナド内包表記に書きなおすときに気をつけるところはmplus $ 3 * 4などをそのまま書くと`<-'がないため「条件」として扱われてしまうことだ。`_ <-`をつけることで「条件じゃないよ」と示す必要がある。

calcC = [ x * 7 | _ <- mplus $ 3 * 4, _ <- mplus $ 2 * 5, x <- mrecall ]

まとめ

Haskellの標準的な機能ではないがGHCの拡張機能としてリスト内包表記を一般的なモナドに使うことができる。do記法では最後に来るreturn expのexpの部分を内包表記では先頭に置くことができる。これは「結論から先」ということであり、最終的な結果が重要なタイプのモナドでは読みやすくなる可能性がある。

「リストモナド」へもどる 「モナド則から導ける規則」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics