Error handling package

While working on the next release of data-object, I wanted to represent some operations that might fail. The typical candidates were: 1) Maybe 2) Either 3) Monad Monad is always iffy because of the often times poorly defined fail. Maybe doesn't provide any means of reporting what the problem was. Either is not defined as a Monad in the base library. Also, the usual candidate of Either String only provides for an error message. It would be nice to have more in-depth information available. So I've put together a package I call "attempt". It defines a data type called (surprise) Attempt with a Success and Failure constructor. The trick here is that it using the new extensible exceptions under the surface, so you can report any kind of exception you want. It also provides a "FromAttempt" type class for possible conversion targets from an Attempt, provides an attempt function with some helpers for performing the equivalent of Control.Exception.catches, and provides two samples of functions I would want to implement with this library: attemptJoin and attemptLookup. My questions for the list are: 1) Is the overall approach sound, or do people have better ideas? 2) Are there any other FromAttempt instances I should provide out of the box? 3) I was considering adding specialized versions of the fromAttempt function, ie ioFromAttempt, maybeFromAttempt. Thoughts? 4) Should I follow the naming scheme attemptJoin, attemptLookup, etc, or just call the functions join, lookup and force people to import the module qualified? 5) Any other suggestions for attempt functions? I've considered head/tail/etc. 6) Include ToAttempt? The code is available on github at http://github.com/snoyberg/attempt/blob/master/Data/Attempt.hs . I appreciate the review. Also, I have not yet documented the code, but I will do so before uploading to Hackage; I just didn't want to document a changing target. Thank you, Michael

On Sun, 18 Oct 2009, Michael Snoyman wrote:
While working on the next release of data-object, I wanted to represent some operations that might fail. The typical candidates were:
1) Maybe 2) Either 3) Monad
Monad is always iffy because of the often times poorly defined fail. Maybe doesn't provide any means of reporting what the problem was. Either is not defined as a Monad in the base library. Also, the usual candidate of Either String only provides for an error message. It would be nice to have more in-depth information available.
So I've put together a package I call "attempt". It defines a data type called (surprise) Attempt with a Success and Failure constructor.
Does the explicit-exception package provide what you need? http://hackage.haskell.org/package/explicit-exception

On Sun, Oct 18, 2009 at 9:45 PM, Michael Snoyman
While working on the next release of data-object, I wanted to represent some operations that might fail. The typical candidates were:
1) Maybe 2) Either 3) Monad
Monad is always iffy because of the often times poorly defined fail. Maybe doesn't provide any means of reporting what the problem was. Either is not defined as a Monad in the base library. Also, the usual candidate of Either String only provides for an error message. It would be nice to have more in-depth information available.
So I've put together a package I call "attempt". It defines a data type called (surprise) Attempt with a Success and Failure constructor. The trick here is that it using the new extensible exceptions under the surface, so you can report any kind of exception you want. It also provides a "FromAttempt" type class for possible conversion targets from an Attempt, provides an attempt function with some helpers for performing the equivalent of Control.Exception.catches, and provides two samples of functions I would want to implement with this library: attemptJoin and attemptLookup.
My questions for the list are:
1) Is the overall approach sound, or do people have better ideas?
I think that there is a place for such a package. Given the Monad/fail issue, the Maybe/no-message issue and the Either/not-a-monad-in-base issue. About the name Attempt, I think that 'Outcome' would be a better name.
2) Are there any other FromAttempt instances I should provide out of the box?
None that I see.
3) I was considering adding specialized versions of the fromAttempt function, ie ioFromAttempt, maybeFromAttempt. Thoughts?
It is a bit long to spell.
4) Should I follow the naming scheme attemptJoin, attemptLookup, etc, or just call the functions join, lookup and force people to import the module qualified?
A nice alternative would be to make a Data.Attempt.Extra (or a better name) which would be imported qualified.
5) Any other suggestions for attempt functions? I've considered head/tail/etc.
Why not, maybe the 'safe' package should be of some inspiration.
6) Include ToAttempt?
IMHO, no.
The code is available on github at
I think that the 'attempt' function should follow functions like 'maybe' and 'either'. attempt :: (forall e. Exception e -> b) -> (a -> b) -> Attempt a -> b Regards, -- Nicolas Pouillard http://nicolaspouillard.fr

On Tue, Oct 20, 2009 at 11:04 AM, Nicolas Pouillard < nicolas.pouillard@gmail.com> wrote:
While working on the next release of data-object, I wanted to represent some operations that might fail. The typical candidates were:
1) Maybe 2) Either 3) Monad
Monad is always iffy because of the often times poorly defined fail. Maybe doesn't provide any means of reporting what the problem was. Either is not defined as a Monad in the base library. Also, the usual candidate of Either String only provides for an error message. It would be nice to have more in-depth information available.
So I've put together a package I call "attempt". It defines a data type called (surprise) Attempt with a Success and Failure constructor. The
On Sun, Oct 18, 2009 at 9:45 PM, Michael Snoyman
wrote: trick here is that it using the new extensible exceptions under the surface, so you can report any kind of exception you want. It also provides a "FromAttempt" type class for possible conversion targets from an Attempt, provides an attempt function with some helpers for performing the equivalent of Control.Exception.catches, and provides two samples of functions I would want to implement with this library: attemptJoin and attemptLookup.
My questions for the list are:
1) Is the overall approach sound, or do people have better ideas?
I think that there is a place for such a package. Given the Monad/fail issue, the Maybe/no-message issue and the Either/not-a-monad-in-base issue.
About the name Attempt, I think that 'Outcome' would be a better name.
The problem with a name like "Outcome" is that it doesn't really imply the possibility of failure, simply that we are wrapping of the result for some reason.
2) Are there any other FromAttempt instances I should provide out of the box?
None that I see.
3) I was considering adding specialized versions of the fromAttempt function, ie ioFromAttempt, maybeFromAttempt. Thoughts?
It is a bit long to spell.
True, but it can be more convenient than giving explicit type signatures. The main reason this might come up would be regarding the (Either String) and (Either SomeException) instances.
4) Should I follow the naming scheme attemptJoin, attemptLookup, etc, or just call the functions join, lookup and force people to import the module qualified?
A nice alternative would be to make a Data.Attempt.Extra (or a better name) which would be imported qualified.
I like. The only other name I can think of right now is Data.Attempt.Helper, but that's just painting the bike shed.
5) Any other suggestions for attempt functions? I've considered head/tail/etc.
Why not, maybe the 'safe' package should be of some inspiration.
Once they're all in their own module, I don't see a problem adding as many as possible.
6) Include ToAttempt?
IMHO, no.
The code is available on github at
I think that the 'attempt' function should follow functions like 'maybe' and 'either'.
attempt :: (forall e. Exception e -> b) -> (a -> b) -> Attempt a -> b
I was trying to more model it after fromMaybe, but instead just ended up
with something ugly. I'm going to rewrite attempt using this type signature and then add fromAttempt as well. Michael

On Tue, Oct 20, 2009 at 3:12 PM, Michael Snoyman
On Tue, Oct 20, 2009 at 11:04 AM, Nicolas Pouillard
wrote: On Sun, Oct 18, 2009 at 9:45 PM, Michael Snoyman
wrote: While working on the next release of data-object, I wanted to represent
[...]
About the name Attempt, I think that 'Outcome' would be a better name.
The problem with a name like "Outcome" is that it doesn't really imply the possibility of failure, simply that we are wrapping of the result for some reason.
I don't see why, the outcome of an action could be either a success or a failure. Even if the only *desired* outcome is success.
2) Are there any other FromAttempt instances I should provide out of the box?
None that I see.
3) I was considering adding specialized versions of the fromAttempt function, ie ioFromAttempt, maybeFromAttempt. Thoughts?
It is a bit long to spell.
True, but it can be more convenient than giving explicit type signatures. The main reason this might come up would be regarding the (Either String) and (Either SomeException) instances.
4) Should I follow the naming scheme attemptJoin, attemptLookup, etc, or just call the functions join, lookup and force people to import the module qualified?
A nice alternative would be to make a Data.Attempt.Extra (or a better name) which would be imported qualified.
I like. The only other name I can think of right now is Data.Attempt.Helper, but that's just painting the bike shed.
I also prefer Helper.
5) Any other suggestions for attempt functions? I've considered head/tail/etc.
Why not, maybe the 'safe' package should be of some inspiration.
Once they're all in their own module, I don't see a problem adding as many as possible.
[...]
attempt :: (forall e. Exception e -> b) -> (a -> b) -> Attempt a -> b
I was trying to more model it after fromMaybe, but instead just ended up with something ugly. I'm going to rewrite attempt using this type signature and then add fromAttempt as well.
Fine, fromAttempt should follow fromMaybe fromAttempt :: (forall e. Exception e -> a) -> Attempt a -> a fromAttempt f a = attempt f id a Regards, -- Nicolas Pouillard http://nicolaspouillard.fr

On Tue, Oct 20, 2009 at 5:17 PM, Nicolas Pouillard < nicolas.pouillard@gmail.com> wrote:
On Tue, Oct 20, 2009 at 3:12 PM, Michael Snoyman
wrote: On Tue, Oct 20, 2009 at 11:04 AM, Nicolas Pouillard
wrote: On Sun, Oct 18, 2009 at 9:45 PM, Michael Snoyman
wrote: While working on the next release of data-object, I wanted to
represent
[...]
About the name Attempt, I think that 'Outcome' would be a better name.
The problem with a name like "Outcome" is that it doesn't really imply the possibility of failure, simply that we are wrapping of the result for some reason.
I don't see why, the outcome of an action could be either a success or a failure. Even if the only *desired* outcome is success.
I think I'm going to stick with Attempt. The only other point that came to me is it's nicer to have a function called "attempt" than "outcome" IMO.
2) Are there any other FromAttempt instances I should provide out of the box?
None that I see.
3) I was considering adding specialized versions of the fromAttempt function, ie ioFromAttempt, maybeFromAttempt. Thoughts?
It is a bit long to spell.
True, but it can be more convenient than giving explicit type signatures. The main reason this might come up would be regarding the (Either String) and (Either SomeException) instances.
4) Should I follow the naming scheme attemptJoin, attemptLookup, etc,
or
just call the functions join, lookup and force people to import the module qualified?
A nice alternative would be to make a Data.Attempt.Extra (or a better name) which would be imported qualified.
I like. The only other name I can think of right now is Data.Attempt.Helper, but that's just painting the bike shed.
I also prefer Helper.
Done. Not full-featured yet, but does provide a nice core.
5) Any other suggestions for attempt functions? I've considered head/tail/etc.
Why not, maybe the 'safe' package should be of some inspiration.
Once they're all in their own module, I don't see a problem adding as many as possible.
[...]
attempt :: (forall e. Exception e -> b) -> (a -> b) -> Attempt a -> b
I was trying to more model it after fromMaybe, but instead just ended up with something ugly. I'm going to rewrite attempt using this type signature and then add fromAttempt as well.
Fine, fromAttempt should follow fromMaybe
fromAttempt :: (forall e. Exception e -> a) -> Attempt a -> a fromAttempt f a = attempt f id a
And of course I now realize this name will conflict with the FromAttempt typeclass ;). I think we'll be safe leaving this function out, considering how trivial it is to implement in terms of attempt. Also, I've added a monad transformer to the github repo. I've tried it out in some local code I have, and it *really* cleans up the error handling. Michael

On Wed, Oct 21, 2009 at 12:02 AM, Michael Snoyman
On Tue, Oct 20, 2009 at 5:17 PM, Nicolas Pouillard
wrote: On Tue, Oct 20, 2009 at 3:12 PM, Michael Snoyman
wrote: On Tue, Oct 20, 2009 at 11:04 AM, Nicolas Pouillard
wrote: On Sun, Oct 18, 2009 at 9:45 PM, Michael Snoyman
wrote: While working on the next release of data-object, I wanted to represent
[...]
About the name Attempt, I think that 'Outcome' would be a better name.
The problem with a name like "Outcome" is that it doesn't really imply the possibility of failure, simply that we are wrapping of the result for some reason.
I don't see why, the outcome of an action could be either a success or a failure. Even if the only *desired* outcome is success.
I think I'm going to stick with Attempt. The only other point that came to me is it's nicer to have a function called "attempt" than "outcome" IMO.
OK [...]
fromAttempt :: (forall e. Exception e -> a) -> Attempt a -> a fromAttempt f a = attempt f id a
And of course I now realize this name will conflict with the FromAttempt typeclass ;). I think we'll be safe leaving this function out, considering how trivial it is to implement in terms of attempt.
:) BTW, fromAttempt instance can be nicely expressed using the 'attempt' function.
Also, I've added a monad transformer to the github repo. I've tried it out in some local code I have, and it *really* cleans up the error handling.
Having the monad transformer too is great. The only minor disadvantage is to make a choice between mtl and transformers. But transformers is slowly replacing mtl so no big deal. -- Nicolas Pouillard http://nicolaspouillard.fr
participants (3)
-
Henning Thielemann
-
Michael Snoyman
-
Nicolas Pouillard