do 記法とリスト内包表記
リスト内包表記はリストモナドの構文糖衣である。
listOfTuples :: [(Int, Char)] listOfTuples = do n <- [1,2] ch <- ['a','b'] return (n, ch)
↓
*Main> [(n, ch) | n <- [1,2], ch <- ['a','b']] [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
MonadPlus と guard 関数
リスト内包表記では、filter をかけることができた。
*Main> [x | x <- [1..50], '7' `elem` show x] [7,17,27,37,47]
7
のつく数字だけを結果として返している。
この動きを解明するためには、MonadPlus と guard 関数を知る必要がある。
MonadPlus はモノイドの性質を併せ持つモナドを表す型クラス。
class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a
mzero
は モノイドでいう mempty
のこと。mplus
は mappend
のこと。リストはモノイドなので、MonadPlus のインスタンスにできる。
instance MonadPlus [] where mzero = [] mplus = (++)
リストのmzero
は失敗した非決定性計算を表現する。mplus
は2つの非決定的値を1つの値に結合する。
guard 関数はこのように
guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero
guard は真理値を引数にとる。引数がTrueの場合()
を成功を表す最小限の文脈に入れる。引数がFalseなら失敗したモナド値を作る。
*Main> import Control.Monad *Main Control.Monad> guard (5 > 2) :: Maybe () Just () *Main Control.Monad> guard (1 > 2) :: Maybe () Nothing *Main Control.Monad> guard (5 > 2) :: [()] [()] *Main Control.Monad> guard (1 > 2) :: [()] []
リストモナドでは、guard を使って解の候補をふるい落とすことができる。
*Main Control.Monad> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x) [7,17,27,37,47]
guard に >>
をつなぐとどうなるか
*Main Control.Monad> guard (5 > 2) >> return "cool" :: [String] ["cool"] *Main Control.Monad> guard (1 > 2) >> return "cool" :: [String] []
guard がやっていることは、引数がFalseなら直ちに失敗を投げる。Trueならダミーの値()が入っている成功を作る。
という計算を続けてよいかの判断をやっている。
do 記法で書き直す
import Control.Monad sevensOnly :: [Int] sevensOnly = do x <- [1..50] guard ('7' `elem` show x) return x
実行
*Main Control.Monad> sevensOnly [7,17,27,37,47]
これと同義
*Main> [x | x <- [1..50], '7' `elem` show x] [7,17,27,37,47]
所感
リスト内包表記はモナドのちからを使って実現されていたことが判明した。

- 作者: MiranLipovaca
- 出版社/メーカー: オーム社
- 発売日: 2017/07/14
- メディア: Kindle版
- 購入: 4人 クリック: 9回
- この商品を含むブログを見る