Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MonadPlus IO isn't a monoid

Instance MonadPlus IO is unique because mzero throws:

Prelude Control.Monad> mzero
*** Exception: user error (mzero)

So accordingly, MonadPlus IO implies that it is also intended for errors.

mzero apparently serves as the identity element if the other action doesn't throw:

Prelude Control.Monad> mzero `mplus` return 0
0
Prelude Control.Monad> return 0 `mplus` mzero
0

But it doesn't when both actions throw:

Prelude Control.Monad> fail "Hello, world!" `mplus` mzero
*** Exception: user error (mzero)
Prelude Control.Monad> mzero `mplus` fail "Hello, world!"
*** Exception: user error (Hello, world!)

So MonadPlus IO is not a monoid.

If it violates MonadPlus laws when user intends errors, what is it actually intended for?

like image 644
Dannyu NDos Avatar asked Jan 26 '23 23:01

Dannyu NDos


1 Answers

IO under mplus is a monoid relative to an equivalence class that identifies exceptions. Not that satisfying. An alternative approach might look like this:

m <|> n = m `catches`
  [ Handler $ \ ~EmptyIO -> n
  , Handler $ \ ~se@(SomeException _) ->
      n `catch` \ ~EmptyIO -> throwIO se ]

The main problem with this approach is that handlers can stack up. When the first action fails, we can't just commit to the second action. A smaller issue is that there's no completely reliable way to determine whether an exception is synchronous (and should be rethrown using throwIO) or asynchronous (in which case we need to rethrow it using throwTo with our own thread ID). So that way lies messes.

like image 98
dfeuer Avatar answered Jan 28 '23 13:01

dfeuer