2016-01-04 『ふつうのHaskellプログラミング』 もくもく読書会(第11章) の振り返り
ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門
- 作者: 青木峰郎,山下伸夫
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2014/10/05
- メディア: オンデマンド (ペーパーバック)
- この商品を含むブログを見る
第11章 モナド
モナドとは何か
Monadクラス
class Monad where (>>=) :: ma -> (a -> m b) -> m b return :: a -> m a
モナド則
(return x) >>= f == f x
m >>= return == m
(m >>= f) >> g = m >>= (\x -> f x >>= g)
Maybeモナド
data Maybe a = Nothing | Just a deriving (Eq, Ord)
- 値は「Nothing」か「Just x」のどちらか
- 対象の値がある場合、「Just x」
- 対象の値がない場合、「Nothing」
alist = association list = 2要素タプルのリスト
Maybeモナドの目的
- Maybe を返す関数を連続して使わなければならないときに便利
- 関数を連続して適用してるうちに結果が途中でNothingになると、それ以降の関数で適用してもNothingとして返ってくる
Maybeモナドの実例
本のサンプルでは以下の処理をしている
- lookup関数を使ってconfigから"database"を検索し結果を取得
- 1の結果からさらにlookup関数を使って"encoding"を検索
case (lookup "database" config) of Just entries -> lookup "encoding" entries Nothing -> Nothing
これを(>>=)
で下記に書き換えれる
lookup "database" config >>= lookup "encoding"
メモ
GHCiで複数行を書く場合は:{
と:}
で囲む。
例:
> :{ > case (lookup "database" config) of > Just entries -> lookup "encoding" entries > Nothing -> Nothing > :}
Maybeモナドの仕組み
Mayboeモナドの(>>=)の実装
instance Monad Maybe where (Just x) >>= f = f x Nothing >>= f = Nothing …略
lookupの例の場合、fが「lookup "encoding"」になる
returnクラスメソッドの使い道
returnの定義
> :t return return :: Monad m => a -> m a
Mayboeモナドのreturnの実装
instance Monad Maybe where return x = Just x …略
- 値aを渡したら「Just a」を返す
- (>>=)で連結している関数の中で結果にモナドを返さないものがある場合に使える
リストモナド
リストモナドの目的
- リストモナドの目的は適用するたびに値の数が増えたり減ったりする関数を連結すること
リストモナドの実例
本のサンプルではファイル名のパターンを展開する。以下の処理をする
- []を使って例えば「img[01].png」とあれば「img0.png」、「img1.png」ができる
- {}を使って例えば「img.{png,jpg}」とあれば「img.png」、「img.jpg」ができる
上記の関数として次のものがある(実装は第13章)
- 1の実装はexpandCharClassでされている
- 2の実装はexpandAltWordsでされている
Maybeモナドの仕組み
リストモナドの実装
instance Monad [] where xs >>= f = conactMap f xs return x = [x]
IOモナド
IOモナドの目的
IOモナドの概念
本の説明だとgetContentsアクションの結果「cs」をputStr cs
で出力する例で説明している
main = do cs <- getContents putStr cs
もしくは
main = getContents >>= putStr
- 入力値「cs」を得るためにgetContentsが先に評価されることを指定している
- getContentsが実行される前の世界とputStrが実行された後の世界は別の値とHaskellでは考える
→ そのためHaskellでは副作用が発生していないと考える
つまりgetContentの場合だと
- 入出力結果 = 読み込んだ文字列
- 入出力を実行した後の世界 = 現状のまま
となり、putStrの場合だと
- 入出力結果 = なし
- 入出力を実行した後の世界 = 文字列の表示
ということか?
※ 入出力結果と言うより関数の処理の結果としての値と言うことかと思われる
> :t getContents getContents :: IO String > :t putStr putStr :: String -> IO ()
IOモナドと(>>)
下記のように書き換えれる
do putStrLn "Hello, world" putStrLn "Hello, again!!!"
↓
putStrLn "Hello, World" >>= (\x -> putStrLn "Hello, again!!!")
↓
putStrLn "Hello, World" >> putStrLn "Hello, again!!!"
入出力以外でのIOモナド
- IOモナドは入出力以外でも使われることがある
- 副作用が必要なものに使える
- 例としてランダム値や現在時刻やOSとのやりとりなどにIOモナドが使われる
モナドの構文
do式
(>>=)
を使った式は全てdo式に書き換えれる
let節
do式内でlet節を使う場合に注意が必要
- インデントをletのlに合わすとコンパイルエラー
do let n = 2 * 16 in print n
- インデントをletのlより右にすればOK
do let n = 2 * 16 in print n
- インデントをletのlに合わす場合は「in」を除くとOK
do let n = 2 * 16 print n
do式とif
本ではインデントをifの「i」に合わすとコンパイルエラーとあるがGHCiで試したら問題なかった。 kome
> :} | do if True | then putStrLn "True" | else putStrLn "False" | :}
ファイルにして読み込んでも問題なし
main = do if True then putStrLn "True" else putStrLn "False"
練習問題
答えの内容で問題なし