あどけない話

インターネットに関する技術的な話など

IO モナド

今日も懲りずにモナドの話です。Haskell/Understanding monadsを読んだ覚え書き。

以下の echo で、bind が何をしているか読み解きます。

echo = getChar >>= putChar

この echo は実際動きます。一方、これ以降のコードは動きません。Haskell 風に書いているだけです。

getChar と putChar は、仮想的な World という引数があると考えれば、副作用はなくなります。

getChar :: World -> (a, World)
putChar :: a -> World -> (b, World)

この World を隠しているのが、IO という抽象的なデータ型だと考えます。

type IO a = World -> (a, World)

すると、getChar と putChar の型はこうなります。

getChar :: IO a
putChar :: a -> IO b

bind は、実は裏でこっそり World をパラメータとして渡しています。

(>>=) :: IO a -> (a -> IO b) -> IO b
(>>=) input output = \world0 ->
  let (c , world1) = input world0
      ((), world2) = output c world1
  in ((), world2)

これは冗長なので、以下でもいいでしょう。

(>>=) :: IO a -> (a -> IO b) -> IO b
(>>=) input output = \world0 ->
  let (c , world1) = input world0 in output c world1

input はタプルを返しますが、bind がその値を c と world1 へ分離し、output へ渡しているのがポイントです。

echo の input は getChar で、output は putChar ですから、中を覗くと以下のようになっています。

echo = \world0 ->
  let (c , world1) = getChar world0
      ((), world2) = putChar c world1
  in ((), world2)

しかし、抽象データ型の IO と bind のお陰で、World が隠れます。

echo = getChar >>= putChar