-
Notifications
You must be signed in to change notification settings - Fork 107
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
Define proper Applicative instances for Node, Tree and GenT #173
Conversation
Please check the instances carefully. I may have made a mistake. |
Tree . pure . pure | ||
(<*>) (Tree mab) (Tree ma) = | ||
Tree $ | ||
(\nab na -> nab <*> na) <$> mab <*> ma |
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 don't like the eta expansion of (<*>)
I would change this to liftA2 (<*>) mab ma
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.
Done! :)
Indeed, these are a bit tricky to get right and the consequences of them being wrong is pretty catastrophic 😆 Perhaps we can test them against the |
There was indeed a problem with my Applicative instance for Tree. I have looked into writing the same tests for |
Here are my test results for
The monad instance is broken too. Not sure if that's expected!? Maybe I should check QuickCheck's |
QuickCheck doesn't have a lawful monad instance either, the reason is that both QuickCheck and Hedgehog use a splittable random number generator underneath so that they can generate infinite sequences. In Hedgehog, I think generating infinite sequences is no longer possible since we moved to the monad transformer. As such we could potentially move to using a |
Admittedly, I'm not too concerned about the lack of lawful instances for |
I would prefer that hedgehog continue to support GHC 7.10 If the other laws are already broken, then it doesn't particularly bother me that |
Note that the
The |
Another issue is that the testsuite succeeds with exit code 0 even when there are failing properties. I think one way way to fix that would be using |
Thanks, I get you now. I've gone and looked at the code in Tree.hs. I don't like the kind of conditional API it has, where which instances you get are dependent on your GHC version, so I'd like the fix that. It's a hard problem though, so I don't know when I'll get around to it. Go ahead with the PR. For now, you could change the |
I intend to work further on this later this week. This is what I have in mind:
|
I had to stick with |
I have no idea why the Travis job with GHC-7.10.3 complains about |
@@ -538,13 +538,16 @@ instance Functor m => Functor (GenT m) where | |||
|
|||
instance Monad m => Applicative (GenT m) where |
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.
Should I document that the instance breaks the laws?
What remains to be done before this can be merged? I'd prefer not to end up fixing merge conflicts… |
This looks good to me in the current state. I'm hesitant to merge it without approval from @jystic , since it touches a lot of critical code that only he truly understands. He is a little less active on GitHub these days, so it might be a little longer yet before this gets merged. Thanks for doing all this work. Don't worry about merge conflicts, we will take care of them for you if they arise. |
This PR is six months old now and I'd love to see it in hedgehog. @jystic, can you please review this? |
hedgehog/test/internals.hs
Outdated
import Test.QuickCheck | ||
import Test.Tasty | ||
import Test.Tasty.ExpectedFailure | ||
import Test.Tasty.QuickCheck |
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 was just thinking that it may be possible to let Hedgehog itself run those tests instead of taking on tasty and friends. I like the simplicity of not having to take on any external dependencies:
{-# LANGUAGE OverloadedStrings #-}
module Main (
main
) where
import Control.Monad (unless)
import System.Exit (exitFailure)
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
main :: IO ()
main = do
results <- sequence [
properties
, regressionTests
]
unless (and results)
exitFailure
properties :: IO Bool
properties =
checkParallel $ Group "Properties" [
("Property A"
, property success)
, ("Property B"
, property discard)
, ("Property C"
, property failure)
]
regressionTests :: IO Bool
regressionTests =
checkParallel $ Group "Regression" $
do
(a, b) <-
[
(1, 1),
(2, 2),
(3, 3)
]
return
("Regression A"
, withTests 1 . property $ a === b)
++
do
(a, b) <-
[
(1, 1),
(2, 2),
(3, 0) -- Uh-oh!
]
return
("Regression B"
, withTests 1 . property $ a === b)
It surely isn't perfect (not sure we can recheck a failing test), but I thought it'd be nice to share.
It also feels kind of weird having to use QC to test Hedgehog internals!
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.
(This is just an idea, and shouldn't delay further the PR.)
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 was just thinking that it may be possible to let Hedgehog itself run those tests instead of taking on tasty and friends. I like the simplicity of not having to take on any external dependencies:
Hmm, my intuition would be you shouldn't base tests for a test framework on the very same test framework. If the test framework has a bug, the internal tests might report false positives.
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.
Well, not that I agree or disagree (and QC itself isn't perfect) but if they do it with compilers, it should be 'safe' doing for tests as well 😅
an existing compiler is used to build a “proto” compiler from the current source code. That “proto” compiler is then used to compile itself, producing a “final” compiler.
― https://fsharp.github.io/2015/09/29/fsharp-compiler-guide.html#bootstrapping
hedgehog/hedgehog.cabal
Outdated
, tasty | ||
, tasty-expected-failure | ||
, tasty-quickcheck | ||
, transformers |
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've been thinking about this for a while. Isn't it kind of oxymoron to use QC to test Hedgehog internals?
Perhaps, something like this allows us to mix and match properties as well as deterministic tests, by just using Hedgehog.
Feeling a bit worried about all the implicit QC arbitrary-magic. @jystic shows how a bug slipped in bos/text library by using default arbitraries. It's on this video at 11:50 (up to 13:09).
/cc @jystic @thumphries
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'm definitely torn on this idea myself, but I took the plunge and self-tested in scala-hedgehog:
I haven't worked out a way that will increase the level of confidence I have that the tests are actually running/working though.
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 haven't worked out a way that will increase the level of confidence I have that the tests are actually running/working though.
That could very well be the case with any other testing tool we might want to use; its runner might not be picking up our tests, etc.
I'm afraid I refuse to depend on QuickCheck even in the tests. Just use hedgehog itself to do the tests please, I don't think there is an issue because trees are only to do with shrinking, not whether the test passes/fails. Other than that I'm happy as long as the comparison tests with |
👍 FWIW, in case it helps, one way of doing this is here: #173 (comment) |
Would you mind pointing out why? I pulled in QuickCheck only to use the handy law properties from
Are you referring to the law |
* Also canonicalize a few Monad instances. * Also add Eq instances for Tree and Node. * As GenT's Monad and Applicative instances aren't lawful the associated tests are marked as ignored. * The testsuite is unbuildable with GHC < 8.0 as a few required Show1/Show instances are only defined for base >= 4.9.
I have made the necessary changes so that I'm to merge, just waiting for a green build. Not sure about the name The idea would be to switch to using Hedgehog itself when we have some way to verify laws, which requires that we can generate functions. |
Relevant issue: https://gitlab.haskell.org/ghc/ghc/issues/14051 The bug causes the following output when building `semigroupoids`: ``` ghc: panic! (the 'impossible' happened) (GHC version 8.2.1 for x86_64-unknown-linux): getUnboxedSumName 513 Call stack: CallStack (from HasCallStack): prettyCurrentCallStack, called at compiler/utils/Outputable.hs:1133:58 in ghc:Outputable callStackDoc, called at compiler/utils/Outputable.hs:1137:37 in ghc:Outputable pprPanic, called at compiler/prelude/KnownUniques.hs:104:5 in ghc:KnownUniques ```
@moodmosaic just pointed me to hedgehog-fn so we could resolve the QuickCheck dependency issue quicker than I anticipated. |
Also canonicalize Monad instances.
This reduces allocations in a test of mine by ~40%.