たとえば、HDBCを使うときに普通にIOモナドの中から直接扱うには、
f :: IO () f = bracket (connectODBC dsn) (disconnect) (\conn -> ...)
のようにすればOKです。ところがこれをIOモナドを合成するモナドの中で扱うとすると、bracketの型がIO a -> (a -> IO b) -> (a -> IO c) -> IO c
なのでbracketの中に入れることができません。例外を無視すればこんな感じに書きたいところなのですが、
g = do conn <- liftIO $ connectODBC dsn ... liftIO $ disconnect conn
こうは書けません。
h = liftIO $ bracket (connectODBC dsn) (disconnect) (\conn -> ...)
そもそもbracketが、MonadIO m => m a -> (a -> m b) -> (a -> m c) -> m c
になっていれば良いのにと思って自前のbracketを定義しようとすると、blockとunblockがIO a -> IO a
なのでうまく定義できません(非同期例外を無視すればblockとunblockなしでも良いような気がしますが)。
その他にも、合成したIOが例外を投げてくる場合には、外側のモナドを一回実行してから再度実行しなおすというのをモナドごとに手で書く必要があってどうしたものか*1と思っていたのですが、そのままずばりの話がHaskell'に出ていました。Generalize types of IO actions。ステータスを見るとmaybeになっているので入ることを期待してます。
*1 そもそもモナドを合成する場合も一つずつ手で書く必要がありますが、例外部分は標準化されていないのでライブラリ側に含まれていないことが多い