モナド
11.4 IOモナド
Maybe、[](リスト)型クラスでは、(>==)とreturnを使ってモナドを理解しました。
そして最後はIOモナドです。
でもやっぱり難しい。ここでもじっくり理解してみます。
評価順序と参照透明性
まずは評価順序。
IOモナドの関数は、戻り値としてIOモナドを返します。
そりゃそうか。IOモナド、つまりアクションを作り出す関数だからね。
たとえばputStrLnの型は、こんな感じ。
Prelude> :t putStrLn putStrLn :: String -> IO ()
IO()っていうのがIOモナド、つまりアクション。でも()って何だろう?
-- Trivial type data () = () deriving (Eq, Ord, Enum, Bounded)
なるほど、何でもいいからとにかくIOモナドクラスのインスタンスを返したいわけですね。
無理やり出力してみると、こんな感じ。
Prelude> putStrLn "Hello, World!" >>= (\x -> print x) Hello, World! ()
なお、本書ではこのアクションを「世界」と呼んでいます。
アクションは世界であり、アクションを評価するとは、
戻り値として入出力の結果を返すと同時に入出力を実行した後の世界を作り出すことなわけです。
putStrLnの例でいえば、文字列が出力された世界が入出力を実行した後の世界なのです。
入出力を実行する前の世界と後の世界は別物。
なので、次にアクションが実行された時は結果が違う。
入力が違うから結果が違う。
そのため、参照透明性が保たれる。
もうちょっと調べてみる
難しいですね。
他の例で言えば、よく出てくるgetContents。
この関数の型は
Prelude> :t getContents getContents :: IO String
ふむ、IO String型を返す関数だそうです。
あれ?本書の例で
main = do cs <- getContents putStrLn cs
というものがありました。
でもgetContentsの戻り値の型とputStrLnの引数の型は違いますよね。
それぞれIO StringとString。
たしかに
Prelude> putStrLn getContents <interactive>:1:9: Couldn't match expected type `String' against inferred type `IO String' In the first argument of `putStrLn', namely `getContents' In the expression: putStrLn getContents In the definition of `it': it = putStrLn getContents
というように単に戻り値を入力しただけでは怒られます。
この謎はdoです。このdoは別の書き方で書くと
main = getContents >>= putStrLn
となります。ここで改めてMonadクラスのクラスメソッド(>>=)の定義を見てみると
class Monad m where (>>=) :: m a -> (a -> m b) -> m b
なるほど、(>>=)で関数を連結すると、
右辺の式の引数はMonadクラスのインスタンスm aからmを取っ払う形で実行されるんですね。
なので、
getContentsの戻り値はIO String。
putStrLnの引数はString。
これは納得です。
今日はここまで!