Absolutely confused with error/exception handling

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

Absolutely confused with error/exception handling

Saurabh Nanda
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. 

_______________________________________________
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Brandon Allbery

On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

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 <[hidden email]> wrote:

On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
unix, openafs, kerberos, infrastructure, xmonad        http://sinenomine.net



--
brandon s allbery kf8nh                               sine nomine associates
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

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" <[hidden email]> 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 <[hidden email]> wrote:

On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
unix, openafs, kerberos, infrastructure, xmonad        http://sinenomine.net



--
brandon s allbery kf8nh                               sine nomine associates
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Matt
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. 

Matt Parsons

On Thu, Dec 15, 2016 at 10:30 PM, Saurabh Nanda <[hidden email]> wrote:
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" <[hidden email]> 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 <[hidden email]> wrote:

On Thu, Dec 15, 2016 at 8:42 PM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
unix, openafs, kerberos, infrastructure, xmonad        http://sinenomine.net



--
brandon s allbery kf8nh                               sine nomine associates
[hidden email]                                  [hidden email]
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.


_______________________________________________
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Brandon Allbery
In reply to this post by Saurabh Nanda

On Thu, Dec 15, 2016 at 10:30 PM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Saurabh Nanda
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" <[hidden email]> wrote:

On Thu, Dec 15, 2016 at 10:30 PM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Brandon Allbery

On Thu, Dec 15, 2016 at 11:50 PM, Saurabh Nanda <[hidden email]> wrote:
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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Saurabh Nanda
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-Action.html#t:ActionCtxT (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.


_______________________________________________
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Brandon Allbery

On Fri, Dec 16, 2016 at 7:30 AM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

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?



On 17 Dec 2016 3:13 am, "Brandon Allbery" <[hidden email]> wrote:

On Fri, Dec 16, 2016 at 7:30 AM, Saurabh Nanda <[hidden email]> 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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Brandon Allbery

On Sat, Dec 17, 2016 at 3:46 AM, Saurabh Nanda <[hidden email]> wrote:
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
[hidden email]                                  [hidden email]
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Chris Wong-2
In reply to this post by Saurabh Nanda
> 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-Action.html#t:ActionCtxT
> (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
_______________________________________________
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Saurabh Nanda

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.

I'm trying to write a "global" error handler which logs unhandled errors to Airbrake along with information from the request object (domain, URL, params, cookies, etc). I understand that I can make this part of my app monad, but if I don't understand why I'm unable to catch errors in MonadIO I'm bound to get stuck again, or implement it incorrectly (for example, what are the resource leaks that were being referred to?)

Also what if a particular handler is not in my app monad? How do I prevent someone from accidentally adding a handler without the airbrake integration?

-- Saurabh. 


_______________________________________________
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.
Reply | Threaded
Open this post in threaded view
|

Re: Absolutely confused with error/exception handling

Brandon Allbery

On Sun, Dec 18, 2016 at 3:45 AM, Saurabh Nanda <[hidden email]> wrote:
if I don't understand why I'm unable to catch errors in MonadIO I'm bound to get stuck again, or implement it incorrectly (for example, what are the resource leaks that were being referred to?)
 
https://mail.haskell.org/pipermail/libraries/2014-November/024224.html is the start of the rather large thread that led to the current exception primitives, which exception managers ranging from the standard `bracket` to MonadBaseControl rely on. There are quite a few examples of resource leaks during exception handling (or despite exception handling!) in it, along with many other pitfalls that can occur.

--
brandon s allbery kf8nh                               sine nomine associates
[hidden email]                                  [hidden email]
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.