-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
pretty-print HUnitFailure exceptions #63
Conversation
test/common/TestCommonEqEpsilon.hs
Outdated
-- The wildcard branch below prints: | ||
-- Hello from exceptionHandler! Exception is: HUnitFailure (Just (SrcLoc {srcLocPackage = "horde-ad-0.1.0.0-inplace-testLibrary", srcLocModule = "TestCommonEqEpsilon", srcLocFile = "test/common/TestCommonEqEpsilon.hs", srcLocStartLine = 118, srcLocStartCol = 5, srcLocEndLine = 118, srcLocEndCol = 16})) (Reason "expected: 0.8991\n but got: 0.7424999999999999\n (maximum margin of error: 1.0e-6)") of type: HUnitFailure | ||
-- BUT we cannot match e with HUnitFailure x y because it doesn't compile: | ||
-- HUnitFailure x y -> putStrLn $ "We got HUnitFailure!" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compile error is:
test/common/TestCommonEqEpsilon.hs:49:5: error:
• Couldn't match expected type ‘e’ with actual type ‘HUnitFailure’
‘e’ is a rigid type variable bound by
a pattern with constructor:
SomeException :: forall e. Exception e => e -> SomeException,
in an equation for ‘exceptionHandler’
at test/common/TestCommonEqEpsilon.hs:44:19-33
• In the pattern: HUnitFailure x y
In a case alternative:
HUnitFailure x y -> putStrLn $ "We got HUnitFailure!"
In a stmt of a 'do' block:
case e of
HUnitFailure x y -> putStrLn $ "We got HUnitFailure!"
_ -> putStrLn
$ "Hello from exceptionHandler! Exception is: "
++ (show e) ++ " of type: " ++ (show $ typeOf e)
• Relevant bindings include
e :: e (bound at test/common/TestCommonEqEpsilon.hs:44:33)
|
49 | HUnitFailure x y -> putStrLn $ "We got HUnitFailure!"
|
and I thought: this is perhaps due to a missing instance, i.e. instance Exception HUnitFailure
. So I tried:
import Test.Tasty.HUnit.Orig ()
(where that instance is declared) but then I got:
test/common/TestCommonEqEpsilon.hs:15:1: error:
Could not load module ‘Test.Tasty.HUnit.Orig’
it is a hidden module in the package ‘tasty-hunit-0.10.0.3’
it is a hidden module in the package ‘tasty-hunit-0.10.0.3’
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
15 | import Test.Tasty.HUnit.Orig ()
|
and if I just declare the instance: instance Exception HUnitFailure
then I get:
test/common/TestCommonEqEpsilon.hs:19:10: error:
Duplicate instance declarations:
instance Exception HUnitFailure
-- Defined at test/common/TestCommonEqEpsilon.hs:19:10
instance [safe] Exception HUnitFailure
-- Defined in ‘tasty-hunit-0.10.0.3:Test.Tasty.HUnit.Orig’
|
19 | instance Exception HUnitFailure
|
Is there a way to pattern match on SomeException (HUnitFailure)
then?...
What is [safe]
?
BTW I just discovered here:
-- | This is the code copied from the original hunit package (v. 1.2.5.2).
-- with minor modifications
but this is probably irrelevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this now explained by there being two copies of HUnitFailure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No.
It is a well-known issue: UnkindPartition/tasty#327 (comment)
EDIT: maybe. Let me see...
EDIT: yes. Let me implement a fix...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SomeException
fails because, with this quantification in place, you need to choose a type e
, which decides at what point of the exception hierarchy you want to be. You choose with https://downloads.haskell.org/ghc/latest/docs/libraries/base-4.17.0.0/Control-Exception.html#v:fromException (or rather, with the type that the context implies in the application of fromException
). In response you get an exception if, indeed, at this point of the hierarchy there is any exception encapsulated inside that expression. I think, If you implied another type e
, you could get a value of another type out (though internally it would be the same untyped value, but you'd get a different API for it).
it is a hidden module
Right, an internal hidden module.
What is [safe]?
No idea.
Is there a way to pattern match on SomeException (HUnitFailure) then?
I don't think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just managed to fix the whole thing without touching any exceptions at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SomeException
fails because, with this quantification in place, you need to choose a typee
, which decides at what point of the exception hierarchy you want to be. You choose with https://downloads.haskell.org/ghc/latest/docs/libraries/base-4.17.0.0/Control-Exception.html#v:fromException (or rather, with the type that the context implies in the application offromException
). In response you get an exception if, indeed, at this point of the hierarchy there is any exception encapsulated inside that expression. I think, If you implied another typee
, you could get a value of another type out (though internally it would be the same untyped value, but you'd get a different API for it).
What do you say? That you cannot pattern match on SomeException
(except the wildcard _
pattern), but you have to explicitly call fromException
instead? Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
Because it's 21st century and we have OOP or at least hierarchies or at least subtyping or at least the type tricks above that emulate subtyping. But seriously, is the way hierarchy is expressed surprising or the purpose unclear or what else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps the crucial bit is extensible hierarchies. You have a virtual datatype of all possible exceptions you can get in your application and any new dependency you link against can add a few cases to that "datatype". That can't be done with normal datatypes. OCaml extensible variant types might suffice, but Haskell's anonymous sums are probably not there yet.
test/common/TestCommonEqEpsilon.hs
Outdated
-- because we do not want to depend on Test.HUnit.Approx . Instead, we're using an outdated copy of HUnit hardcoded into | ||
-- Tasty (Test.Tasty.HUnit). | ||
-- | ||
-- Copyright (c) 2014, Richard Eisenberg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @goldfirere , I have stolen some of your code. Hope you're OK with that?... This is to get rid of dependency on HUnit-approx
(Tasty
contains its own copy of HUnit
and if we have both, it leads to problems).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me. Please feel free to merge now in whichever of the 3 ways.
It's a pity we have to copy-paste code, but that's the price for the design decision in Tasty that makes them safe from PVP violations from re-exporting Hunit modules, IIUC (UnkindPartition/tasty#327).
Quick! Before @goldfirere tells you this doesn't make any sense. |
I've pushed to master, so the 'Merge PR' method now requires a local rebase. |
Rebase does not exactly work: https://stackoverflow.com/questions/19016698/git-branch-diverged-after-rebase , ok to merge |
This SO thread says that it works fine with
I'd prefer not to, because it complicates git history and it becomes harder to read and harder to bisect. Unless you want to squash afterwards, but if so, you don't need to merge at all. |
… the latter This is because we do not want to depend on Test.HUnit.Approx . Instead, we're using an outdated copy of HUnit hardcoded into Tasty (Test.Tasty.HUnit). If we mix the two, we get two copies of HUnitFailure exception which leads to trouble.
5598815
to
0e31549
Compare
I can't see why is |
Thank you. It's a matter of taste. Some people prefer merges to rebases. With many long-lived concurrent features branches, rebases become a nightmare, but with merges the history becomes a nightmare. It's a bit similar to "should programs be optimized for writing or for reading". Do we agree, however, that the effect of |
Yes, sure. I would have less reservation towards |
Fair enough.
I did. With both, but most of all with reading and understanding the intention of PRs, because they are no longer self contained entities, but amalgams with whatever else they merge. |
I got rid of @goldfirere 's code, making this PR even smaller. OK to merge, @Mikolaj ? |
Well done. Please do. |
@sfindeisen: Thanks a lot for the PR. This now complements #57 to become a comprehensive solution to UnkindPartition/tasty#337 that can be added on top of Tasty and that does not use HUnit nor any other non-base and non-tasty packages. |
@Mikolaj thanks. |
Implements pretty printing of
HUnitFailure
exceptions by removing the dependency onHUnit-approx
.Fixes #60