モナド

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。
これは納得です。


今日はここまで!