
On Sun, Feb 13, 2005 at 01:31:56PM -0500, David Roundy wrote:
On Sun, Feb 13, 2005 at 04:57:46PM +0100, Remi Turk wrote:
According to http://www.haskell.org/hawiki/MonadPlus (see also the recent thread about MonadPlus) a MonadPlus instance should obey m >> mzero === mzero, which IO doesn't. IOW, the MonadPlus instance for IO (defined in Control.Monad.Error) probably shouldn't be there.
True. In the IO monad there are side effects that don't get "erased" when a later action raises an exception as that law would suggest. But any IO-like monad that I'm likely to implement will have the same discrepancy, and in any IO code that catches "enough" exceptions to be bug-free will be immune to this issue.
Basically, the issue is that
do { writeFile "foo" "bar"; writeFile "bar" "foo" } `catch` \_ -> putStr "Couldn't create file\m"
may reach the putStr with or without the file "foo" existing, and there's no way to know whether or not it was created. But that just means the code was written sloppily--that is, if the existence of that foo file is important.
In my uses of MonadPlus, I'd have other schemes essentially immitating IO, so they'd duplicate this behavior (later errors don't undo earlier actions), and well-written functions would depend on that.
But what if `instance MonadPlus IO' disappears from the libraries some day? (which it should, IMO)
It might be interesting to write a "backtracking" IO-like monad which obeyed m >> mzero === mzero. I imagine you could do it for something like an ACID database, if you define === as meaning "has the same final result on the database", which of course would only be useful if the database had sufficient locking that it couldn't have been read between the original m and the later mzero.
You might be interested in the recent STM monad then (Control.Concurrent.STM in GHC-6.4): `T' for Transactional. However, though it supports both MonadPlus and exceptions, it doesn't use MonadPlus for exceptions: It's used for blocking/retrying a thread/transaction. I never used it, so I'm not sure whether it makes any sense, but wouldn't MonadError be a better candidate class to base it upon? Greetings, Remi -- Nobody can be exactly like me. Even I have trouble doing it.