I'm trying to understand how shrink works in Haskell's QuickCheck, so I've come up with a minimal example shown below, but when I run it, the falsified examples are not shrunk.
The result object shows that no shrinks where tried, and adding a "trace" to the shrink definition doesn't print anything.
So why are the counterexamples not shrunk? Why is 'shrink' never called?
import Debug.Trace
import Test.QuickCheck
simpleString = listOf1 $ choose ('a','d')
newtype SimpleString = SimpleString String deriving (Show)
splits l = [splitAt i l | i <- [0..length l]]
removeOne l = [x ++ drop 1 y | (x,y) <- splits l, y /= []]
instance Arbitrary SimpleString where
arbitrary = fmap SimpleString simpleString
shrink ss | trace ("shrink " ++ show ss) False = undefined
shrink (SimpleString []) | trace " shrink []" True = [SimpleString ""]
shrink (SimpleString ss) | trace (" shrink " ++ show ss) True = [SimpleString ss' | ss' <- removeOne ss ]
numberOf c l = length $ filter (== c) l
prop = forAll arbitrary $ \(SimpleString s) -> length s > 5 ==> numberOf 'a' s /= numberOf 'b' s
Every sample run with quickCheckResult prop
yields something like the following:
*** Failed! Falsified (after 4 tests):
SimpleString "aaccbb"
Failure {numTests = 4, numDiscarded = 190, numShrinks = 0, numShrinkTries = 0, numShrinkFinal = 0, usedSeed = SMGen 8220098710381543270 7510598040095200595, usedSize = 6, reason = "Falsified", theException = Nothing, output = "*** Failed! Falsified (after 4 tests):\nSimpleString \"aaccbb\"\n", failingTestCase = ["SimpleString \"aaccbb\""], failingLabels = [], failingClasses = fromList []}
Note that numShrinks = 0, numShrinkTries = 0 always. Why is this?
Thanks
PS: I'm running ghc 8.8.3 with QuickCheck 2.14.2 on macOS.