Absolutely confused with error/exception handling

Hi, What started as a simple exercise of writing an Airbrake integration for Spock, has turned into a day of reading about errors, Control.Exception, MonadThrow, MonadCatch, sync, asynchronous, etc. Even after all that reading I haven't been able to find answers to the following -- * What exactly is done by the "error" function? How does one "trap" those errors and react to it? Can "catch" trap those errors? What is "e" in the case of errors raised by "error"? * How is "error" different from "throw" and "throwIO" (in the Control.Exception package, I believe) * What does MonadThrow and MonadCatch bring to the table? My understanding is that they are bringing a unified API to representing short-circuitable computations based on the inherently short-circuitable value of the host monad. Eg throwM in Maybe just results in a Nothing. throwM in List results in [] Therefore, in most of the cases throwM is not really using the error machinery built into the Haskell language (what said machinery is, is the very first question that I still don't completely understand) All of this might be obvious, and I might have just reached a mind-block after hours of reading. Help would be really appreciated :) -- Saurabh.

On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda
* What exactly is done by the "error" function? How does one "trap" those errors and react to it? Can "catch" trap those errors? What is "e" in the case of errors raised by "error"?
`error` specifically raises a UserError exception, with a user-specified payload (message). `catch` can catch them as such.
* How is "error" different from "throw" and "throwIO" (in the Control.Exception package, I believe)
`throw` (for pure code / asynchronous) and `throwIO` (for code in IO) can throw any exception. No guarantees are made as to when the exception thrown by `throw` -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

whoops, pressed wrong key...
No guarantees are made as to when the exception thrown by `throw` occurs;
this allows it to be thrown from pure code. `throwIO` is sequenced as any
other I/O operation, so you have some guarantee as to I/O operations before
the `throwIO` being performed.
On Thu, Dec 15, 2016 at 9:28 PM, Brandon Allbery
On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda
wrote: * What exactly is done by the "error" function? How does one "trap" those errors and react to it? Can "catch" trap those errors? What is "e" in the case of errors raised by "error"?
`error` specifically raises a UserError exception, with a user-specified payload (message). `catch` can catch them as such.
* How is "error" different from "throw" and "throwIO" (in the Control.Exception package, I believe)
`throw` (for pure code / asynchronous) and `throwIO` (for code in IO) can throw any exception. No guarantees are made as to when the exception thrown by `throw`
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Thanks. The air is clearing up a bit.
Any reason for "error" and "catch" to be in different packages? Or, why
isn't "catch" in prelude?
"catch" needs a action in IO. What if I have an action in MonadIO?
-- Saurabh.
On 16 Dec 2016 8:00 am, "Brandon Allbery"
whoops, pressed wrong key...
No guarantees are made as to when the exception thrown by `throw` occurs; this allows it to be thrown from pure code. `throwIO` is sequenced as any other I/O operation, so you have some guarantee as to I/O operations before the `throwIO` being performed.
On Thu, Dec 15, 2016 at 9:28 PM, Brandon Allbery
wrote: On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda
wrote: * What exactly is done by the "error" function? How does one "trap" those errors and react to it? Can "catch" trap those errors? What is "e" in the case of errors raised by "error"?
`error` specifically raises a UserError exception, with a user-specified payload (message). `catch` can catch them as such.
* How is "error" different from "throw" and "throwIO" (in the Control.Exception package, I believe)
`throw` (for pure code / asynchronous) and `throwIO` (for code in IO) can throw any exception. No guarantees are made as to when the exception thrown by `throw`
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Hi Saurabh,
I've found that the `safe-exceptions` package [1] and related articles [2]
to be some of the best reading around exceptions in Haskell.
To answer your third question, the `IO` instance for
`MonadThrow`/`MonadCatch`/etc. do use Haskell's extensible exception
machinery. The monad-ste [3] package uses Haskell's exception hierarchy in
a controlled manner where evaluating `Either` ends up being expensive.
I've also found exceptions to be one of the hardest parts about Haskell.
Interestingly, they're one of the few dynamically typed bits with nearly
OO-style inheritance.
[1]: https://hackage.haskell.org/package/safe-exceptions
[2]: https://haskell-lang.org/tutorial/exception-safety
[3]: https://hackage.haskell.org/package/monad-ste
Matt Parsons
On Thu, Dec 15, 2016 at 10:30 PM, Saurabh Nanda
Thanks. The air is clearing up a bit.
Any reason for "error" and "catch" to be in different packages? Or, why isn't "catch" in prelude?
"catch" needs a action in IO. What if I have an action in MonadIO?
-- Saurabh.
On 16 Dec 2016 8:00 am, "Brandon Allbery"
wrote: whoops, pressed wrong key...
No guarantees are made as to when the exception thrown by `throw` occurs; this allows it to be thrown from pure code. `throwIO` is sequenced as any other I/O operation, so you have some guarantee as to I/O operations before the `throwIO` being performed.
On Thu, Dec 15, 2016 at 9:28 PM, Brandon Allbery
wrote: On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda
wrote: * What exactly is done by the "error" function? How does one "trap" those errors and react to it? Can "catch" trap those errors? What is "e" in the case of errors raised by "error"?
`error` specifically raises a UserError exception, with a user-specified payload (message). `catch` can catch them as such.
* How is "error" different from "throw" and "throwIO" (in the Control.Exception package, I believe)
`throw` (for pure code / asynchronous) and `throwIO` (for code in IO) can throw any exception. No guarantees are made as to when the exception thrown by `throw`
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
_______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On Thu, Dec 15, 2016 at 10:30 PM, Saurabh Nanda
Any reason for "error" and "catch" to be in different packages? Or, why isn't "catch" in prelude?
"catch" needs a action in IO. What if I have an action in MonadIO?
A version of `catch` used to be in Prelude, but it *only* worked with the IOException subtype; the one in Control.Exception works for all exception types. Rather than propagate the confusion between them, or risk breaking code in weird ways (the one in Prelude was restricted in part because the exception system was redesigned to be extensible, and the Prelude version didn't handle the way the new exception types work), it was simply removed. The Prelude one was something of an oddity even before extensible exceptions and mostly confused new Haskellers, so there didn't seem to be much reason to keep it around. MonadIO is going to be hard. It's not a type but a typeclass for things that can perform operations in IO; but this is not (and can't be, if you think about it) bidirectional. Consider the case of StateT s IO: your exception handler can get an `s` known when the `catch` is evaluated, but can never know the *current* value of the `s` when the exception is thrown --- exceptions do not know how to carry state around like that, and in the most general case can't know how. There are various frameworks that attempt to add this kind of functionality, such as MonadCatch and MonadBaseControl; but it's fairly tricky even at the best of times. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Consider the case of StateT s IO: your exception handler can get an `s` known when the `catch` is evaluated, but can never know the *current* value of the `s` when the exception is thrown --- exceptions do not know how to carry state around like that, and in the most general case can't know how. There are various frameworks that attempt to add this kind of functionality, such as MonadCatch and MonadBaseControl; but it's fairly tricky even at the best of times.
Apologies, but I couldn't understand the example. In an exception where
it's not even possible to know the stack trace easily, I don't expect to
know the state of the computation. It's an exception -- it is alright if it
doesn't have the state. But at least allow me to catch the exception in
MonadIO.
And this brings me to my original question. What do I need to do to catch
an exception in spock's ActionT? I thought I didn't understand something
earlier. But now it seems that I have hit a hairy part of Haskell.
--Saurabh.
On 16 Dec 2016 9:14 am, "Brandon Allbery"
On Thu, Dec 15, 2016 at 10:30 PM, Saurabh Nanda
wrote: Any reason for "error" and "catch" to be in different packages? Or, why isn't "catch" in prelude?
"catch" needs a action in IO. What if I have an action in MonadIO?
A version of `catch` used to be in Prelude, but it *only* worked with the IOException subtype; the one in Control.Exception works for all exception types. Rather than propagate the confusion between them, or risk breaking code in weird ways (the one in Prelude was restricted in part because the exception system was redesigned to be extensible, and the Prelude version didn't handle the way the new exception types work), it was simply removed. The Prelude one was something of an oddity even before extensible exceptions and mostly confused new Haskellers, so there didn't seem to be much reason to keep it around.
MonadIO is going to be hard. It's not a type but a typeclass for things that can perform operations in IO; but this is not (and can't be, if you think about it) bidirectional. Consider the case of StateT s IO: your exception handler can get an `s` known when the `catch` is evaluated, but can never know the *current* value of the `s` when the exception is thrown --- exceptions do not know how to carry state around like that, and in the most general case can't know how. There are various frameworks that attempt to add this kind of functionality, such as MonadCatch and MonadBaseControl; but it's fairly tricky even at the best of times.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Thu, Dec 15, 2016 at 11:50 PM, Saurabh Nanda
Apologies, but I couldn't understand the example. In an exception where it's not even possible to know the stack trace easily, I don't expect to know the state of the computation. It's an exception -- it is alright if it doesn't have the state. But at least allow me to catch the exception in MonadIO.
Use liftIO for that; that's what MonadIO is for. I expected you were needing to go the other way, because if you have MonadIO then you have IO via liftIO. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

Use liftIO for that; that's what MonadIO is for. I expected you were needing to go the other way, because if you have MonadIO then you have IO via liftIO.
The type signature for catch is -- catch :: Exception e => IO a -> (e -> IO a) -> IO a This is Spock's monad: https://hackage.haskell.org/package/Spock-core-0.11.0.0/docs/Web-Spock-Actio... (it has MonadTrans, MonadIO, Monad, Functor, Applicative, Alternative instances). How do I catch an exception coming out of an action in the ActionCtxT monad? -- Saurabh.

On Fri, Dec 16, 2016 at 7:30 AM, Saurabh Nanda
How do I catch an exception coming out of an action in the ActionCtxT monad?
I think I'm going to have to let someone else answer. I just got lost in MonadBaseControl, not to mention how you do this if all you have is the ActionCtxT and not the WebStateT where Spock actually handles exceptions. :/ If it helps, exception handling is known to be a sewer --- because it can't be done in a clean and generic way without introducing significant resource leaks and other problems. And MonadBaseControl works when you (or the developer, in this case of Spock) define the right instances, but is doing it the hard way. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

To generalise the problem, because I've encountered it in other places as
well, how does one write his/her own instance of MonadIO, MonadThrow, or
MonadCatch for complicated transformer stacks such has ActionCtxT?
On 17 Dec 2016 3:13 am, "Brandon Allbery"
On Fri, Dec 16, 2016 at 7:30 AM, Saurabh Nanda
wrote: How do I catch an exception coming out of an action in the ActionCtxT monad?
I think I'm going to have to let someone else answer. I just got lost in MonadBaseControl, not to mention how you do this if all you have is the ActionCtxT and not the WebStateT where Spock actually handles exceptions. :/
If it helps, exception handling is known to be a sewer --- because it can't be done in a clean and generic way without introducing significant resource leaks and other problems. And MonadBaseControl works when you (or the developer, in this case of Spock) define the right instances, but is doing it the hard way.
-- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

On Sat, Dec 17, 2016 at 3:46 AM, Saurabh Nanda
To generalise the problem, because I've encountered it in other places as well, how does one write his/her own instance of MonadIO, MonadThrow, or MonadCatch for complicated transformer stacks such has ActionCtxT?
You do not generally write MonadIO, you use GeneralizedNewtypeDeriving. But if you insist on writing it yourself: instance MonadIO MyMonad ... where liftIO = lift . liftIO (your MyMonad *must* be an instance of MonadTrans) MonadThrow and MonadCatch, I suggest you refer to their documentation. But I will tell you that writing this kind of exception management, in such a way that you do not leak resources (memory, file handles, database connections, ...), is *very hard*. You probably should not attempt it unless you have experience with this. (I won't even try to write one; I understand the basic issues, but lack specific experience with how to deal with them in ghc and with specific exception mechanisms like MonadThrow/MonadCatch and MonadBaseControl.) -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

The type signature for catch is --
catch :: Exception e => IO a -> (e -> IO a) -> IO a
This is Spock's monad: https://hackage.haskell.org/package/Spock-core-0.11.0.0/docs/Web-Spock-Actio... (it has MonadTrans, MonadIO, Monad, Functor, Applicative, Alternative instances).
How do I catch an exception coming out of an action in the ActionCtxT monad?
The problem is: ActionCtxT includes a custom error system, which is orthogonal to that provided by exceptions. So to handle exceptions in such a monad, you'd need to bend over backwards to make these two systems work together. To be honest I'm not even sure if this is possible, at least in a way that doesn't have surprising edge cases. My recommendation is to catch the exception in IO first, before it is lifted into the ActionCtxT. If your business logic is decoupled from the web framework (as it should be) then it should be easy enough to do this. -- Chris Wong (https://lambda.xyz) "I had not the vaguest idea what this meant and when I could not remember the words, my tutor threw the book at my head, which did not stimulate my intellect in any way." -- Bertrand Russell
participants (4)
-
Brandon Allbery
-
Chris Wong
-
Matt
-
Saurabh Nanda