Proposal: Add Eq instance for Control.Exception.ErrorCall

Hi, I propose to add an Eq instance for ErrorCall. The main motivation is to make it more convenient to construct predicates that select specific exceptions. My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.: evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list") But I think it can be useful in other situations and it is consistent with the fact that other common exception types have an Eq instance (e.g. ArithException, IOException, ExitCode). Discussion period: 3 Weeks Cheers, Simon [1] http://hspec.github.com/

On Tue, 27 Nov 2012, Simon Hengel wrote:
I propose to add an Eq instance for ErrorCall. The main motivation is to make it more convenient to construct predicates that select specific exceptions.
My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.:
evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list")
If this is an actual use case, then there is something very wrong. Handling non-empty lists can be done cleanly using various non-empty list types. Or you avoid 'head' by using 'case' or 'viewL'. If instead you plan to catch something then you should use Either, ExceptionalT, ErrorT or IO exceptions. I am afraid that an Eq instance for ErrorCall promotes the abuse of 'error'.

Henning,
I propose to add an Eq instance for ErrorCall. The main motivation is to make it more convenient to construct predicates that select specific exceptions.
My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.:
evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list")
If this is an actual use case, then there is something very wrong. Handling non-empty lists can be done cleanly using various non-empty list types. Or you avoid 'head' by using 'case' or 'viewL'. If instead you plan to catch something then you should use Either, ExceptionalT, ErrorT or IO exceptions. I am afraid that an Eq instance for ErrorCall promotes the abuse of 'error'.
Please don't get me wrong. Personally I always prefer pattern matching over /exception throwing functions/. However, I still think making it hard to "deal" with exception throw function is the wrong approach. Regarding testing: Failure behavior is part of the public interface of a function. If a function throws an exception (whether I like it or not), I'd like to have it documented. The best way to document something is a test case (IMHO). Cheers, Simon

On Tue, 27 Nov 2012, Simon Hengel wrote:
If this is an actual use case, then there is something very wrong. Handling non-empty lists can be done cleanly using various non-empty list types. Or you avoid 'head' by using 'case' or 'viewL'. If instead you plan to catch something then you should use Either, ExceptionalT, ErrorT or IO exceptions. I am afraid that an Eq instance for ErrorCall promotes the abuse of 'error'.
Please don't get me wrong. Personally I always prefer pattern matching over /exception throwing functions/. However, I still think making it hard to "deal" with exception throw function is the wrong approach.
If you use the exception techniques that I enumerated it is easy to handle those exceptions.
Regarding testing: Failure behavior is part of the public interface of a function. If a function throws an exception (whether I like it or not), I'd like to have it documented. The best way to document something is a test case (IMHO).
In my opinion the better way is to document it in the type. This is what the Error like monads are about.

* Simon Hengel
Please don't get me wrong. Personally I always prefer pattern matching over /exception throwing functions/. However, I still think making it hard to "deal" with exception throw function is the wrong approach.
Regarding testing: Failure behavior is part of the public interface of a function. If a function throws an exception (whether I like it or not), I'd like to have it documented. The best way to document something is a test case (IMHO).
Isn't the person who writes tests usually the same as the person who writes the code which is being tested? So, if writing tests for ErrorCall is inconvenient, that would (hopefully) encourage people to write their own, meaningful, exceptions. Roman

On Tue, Nov 27, 2012 at 1:12 PM, Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Tue, 27 Nov 2012, Simon Hengel wrote:
I propose to add an Eq instance for ErrorCall. The main motivation is
to make it more convenient to construct predicates that select specific exceptions.
My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.:
evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list")
If this is an actual use case, then there is something very wrong. Handling non-empty lists can be done cleanly using various non-empty list types. Or you avoid 'head' by using 'case' or 'viewL'. If instead you plan to catch something then you should use Either, ExceptionalT, ErrorT or IO exceptions. I am afraid that an Eq instance for ErrorCall promotes the abuse of 'error'.
+1 on the proposal. Even if I'm opposed to partial functions, I'm in favor of making it easier to test them reliably. Michael

On Tue, 27 Nov 2012, Michael Snoyman wrote:
+1 on the proposal. Even if I'm opposed to partial functions, I'm in favor of making it easier to test them reliably.
How can you be sure that you get a certain error? Depending on the order of evaluation you might get different error messages if there are multiple possible ways to fail. I think automated testing against specific error messages will not work reliably.

On Tue, Nov 27, 2012 at 1:59 PM, Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Tue, 27 Nov 2012, Michael Snoyman wrote:
+1 on the proposal. Even if I'm opposed to partial functions, I'm in
favor of making it easier to test them reliably.
How can you be sure that you get a certain error? Depending on the order of evaluation you might get different error messages if there are multiple possible ways to fail. I think automated testing against specific error messages will not work reliably.
Sure, in some cases that's true. The idea would be to structure the test case in such a way that we no with certainty which way it will fail. Simon's initial example will always fail in the same way. Michael

On Tue, 27 Nov 2012, Michael Snoyman wrote:
Sure, in some cases that's true. The idea would be to structure the test case in such a way that we no with certainty which way it will fail. Simon's initial example will always fail in the same way.
The message for head of empty list may be changed without warning ...

On Tue, Nov 27, 2012 at 2:22 PM, Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Tue, 27 Nov 2012, Michael Snoyman wrote:
Sure, in some cases that's true. The idea would be to structure the test
case in such a way that we no with certainty which way it will fail. Simon's initial example will always fail in the same way.
The message for head of empty list may be changed without warning ...
That argument can be applied to any form of automated testing. doSomething :: Int -> Int doSomething 5 `shouldBe` 6 If the behavior of doSomething suddenly changes, then your test will fail. In fact, that's one of the *advantages* of having a test suite: it catches these kinds of unexpected changes. Michael

How can you be sure that you get a certain error? Depending on the order of evaluation you might get different error messages if there are multiple possible ways to fail. I think automated testing against specific error messages will not work reliably.
If the function/value under specification is so "insane" that it contains a /set of exceptions/, then you have to be prepared to get any of those [1]: evaluate value `shouldThrow` (||) <$> (== ErrorCall "foo") <*> (== ErrorCall "bar") Again, I'm not implying that it's a good idea to write a function that behave like that. But if you do, I'd at least like to have it documented ;) Cheers, Simon [1] http://research.microsoft.com/en-us/um/people/simonpj/papers/imprecise-exn.h...

On Tue, 27 Nov 2012, Simon Hengel wrote:
If the function/value under specification is so "insane" that it contains a /set of exceptions/, then you have to be prepared to get any of those [1]:
evaluate value `shouldThrow` (||) <$> (== ErrorCall "foo") <*> (== ErrorCall "bar")
Again, I'm not implying that it's a good idea to write a function that behave like that. But if you do, I'd at least like to have it documented ;)
My advice to programmers is: If you are starting to document insane behavior, wait a minute and think about how to avoid it. In many cases this helps. :-) Having said that you might assist the users of hspec by providing variants of shouldThrow, say shouldCallError, that matches error messages. Additionally I would add a DEPRECATE pragma to shouldCallError with a message that tells the programmer that he should think twice.

On 27/11/12 10:59, Simon Hengel wrote:
Hi, I propose to add an Eq instance for ErrorCall. The main motivation is to make it more convenient to construct predicates that select specific exceptions.
My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.:
evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list")
But I think it can be useful in other situations and it is consistent with the fact that other common exception types have an Eq instance (e.g. ArithException, IOException, ExitCode).
Discussion period: 3 Weeks
Already did it, a few weeks ago: http://www.haskell.org/pipermail/cvs-libraries/2012-October/016043.html Cheers, Simon

There are a number of other data types in that module that have a similar
construction IIRC, such as AssertionFailed, NoMethodError,
PatternMatchFail, RecConError, RelSelError, etc. which should probably pick
up Eq, Ord for consistency (and become newtypes).
and a few others that can have the obvious trivial, empty Eq, Ord like
NonTermination, NestedAtomically, BlockedIndefinitelyOnMVar.
The module is currently a bit ad hoc about which types support Eq and Ord.
For instance, ArrayException already supported this kind of Eq/Ord, and all
the multiple constructor versions did, but none of the single-constructor
trivial versions and only some of the reasonable-to-newtype ones.
-Edward
On Tue, Nov 27, 2012 at 8:57 AM, Simon Marlow
On 27/11/12 10:59, Simon Hengel wrote:
Hi, I propose to add an Eq instance for ErrorCall. The main motivation is to make it more convenient to construct predicates that select specific exceptions.
My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.:
evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list")
But I think it can be useful in other situations and it is consistent with the fact that other common exception types have an Eq instance (e.g. ArithException, IOException, ExitCode).
Discussion period: 3 Weeks
Already did it, a few weeks ago:
http://www.haskell.org/**pipermail/cvs-libraries/2012-** October/016043.htmlhttp://www.haskell.org/pipermail/cvs-libraries/2012-October/016043.html
Cheers, Simon
______________________________**_________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/**mailman/listinfo/librarieshttp://www.haskell.org/mailman/listinfo/libraries

On Tue, Nov 27, 2012 at 01:57:11PM +0000, Simon Marlow wrote:
On 27/11/12 10:59, Simon Hengel wrote:
Hi, I propose to add an Eq instance for ErrorCall. The main motivation is to make it more convenient to construct predicates that select specific exceptions.
My current use case is testing for expected exceptions. In Hspec[1] we use predicates for that, e.g.:
evaluate (head []) `shouldThrow` (== ErrorCall "Prelude.head: empty list")
But I think it can be useful in other situations and it is consistent with the fact that other common exception types have an Eq instance (e.g. ArithException, IOException, ExitCode).
Discussion period: 3 Weeks
Already did it, a few weeks ago:
http://www.haskell.org/pipermail/cvs-libraries/2012-October/016043.html
Thanks :)
participants (6)
-
Edward Kmett
-
Henning Thielemann
-
Michael Snoyman
-
Roman Cheplyaka
-
Simon Hengel
-
Simon Marlow