In combination with the voice response that I found useful, a Reader converter with the Writer monad is used:
type Predicate r = ReaderT r (Writer String) Bool
A โshared reading environmentโ is a valid input in this case. Then you can create properties like this:
inv_even :: Predicate Int inv_even = do lift . tell $ "this is the even invariant" (==) 0 . flip mod 2 <$> ask toLabeledProp :: r -> Predicate r -> Property toLabeledProp cause r = let (effect, msg) = runWriter . (runReaderT r) $ cause in printTestCase ("inv: " ++ msg) . property $ effect
and association:
fromPredicates :: [Predicate r] -> r -> Property fromPredicates predicates cause = conjoin . map (toLabeledProp cause) $ predicates
I suspect that there is another approach related to something similar to Either WriterT or here, which will be compressed to compose predicates of different types into one result. But at least this allows you to document the properties that impose different post-conventions, depending on the value of the input.
Edit: this idea spawned a library: http://github.com/jfeltz/quickcheck-property-comb
source share