今日も懲りずにモナドの話です。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