Life Goes On

まあまあだけど楽しんでる方です

(>>) :: (Monad m) => m a -> m b -> m b

最近 IO 周りが騒がしいので、鋭意モナドを勉強中です。
個人的に do 記法に馴染めないので、ここを見て脱糖を試みているのですが、どうも上手く動きません。
もともとはこんなコード。
関数 changed の中で、配列の読み込みをした後に書き込みをしています。返すのは書き込み前の値(False)。

import Control.Monad
import Data.Array.IO

main = print =<< changed =<< ini

ini :: IO (IOArray Int Bool)
ini = newArray (0, 0) False

changed :: IOArray Int Bool -> IO Bool
changed a = do
    b <- readArray a 0
    when (not b) $ writeArray a 0 True
    return b

これを (>>) を使って書き換えると、書き込み後の値(True)が返ってしまいます。
どうやら b が2回実行されてしまうようです。
もともとのコードと等価にする(実行を1回に抑える)にはどうしたらよいのでしょうか?

changed :: IOArray Int Bool -> IO Bool
changed a = (flip when (writeArray a 0 True) . not =<< mb) >> mb
    where mb = readArray a 0

[追記] そうか、 IO の世界に mb (readArray) が2回出てくるから駄目なんだ。こう書き換えれば上手くいく。

changed :: IOArray Int Bool -> IO Bool
changed a = f =<< readArray a 0
    where f b = when (not b) (writeArray a 0 True) >> return b