I have come across a few instances in my testing with QuickCheck when it would have simplified things to write my own modifiers in some cases, but I'm not exactly sure how one would do this. In particular, it would be helpful to know how to write a modifier for generators of lists and of numerics (such as Int
). I'm aware of NonEmptyList
, and Positive
and NonNegative
, that are already in the library, but in some instances it would have made my tests clearer if I could have specified something like a list that is not only NonEmpty, but also NonSingleton (so, it has at least 2 elements), or an Int
that is greater than 1, not just NonZero
or Positive
, or an Int(egral)
that is even/odd, etc.

- 225,310
- 48
- 427
- 736

- 1,314
- 1
- 13
- 33
-
Have you looked at how the existing ones were written? What happened when you tried to write similar code to that yourself? – Daniel Wagner Jun 04 '19 at 05:52
-
@DanielWagner I did look at the definitions of Positive and NonEmptyList, but I didn't feel like I came away from that with a full picture of the methods that could be employed, and I also thought asking the question here on SO might make it easier for others to get a handle on this skill more quickly in the future. – josiah Jun 04 '19 at 13:37
1 Answers
There's plenty of way in which you can do that. Here's some examples.
Combinator function
You can write a combinator as a function. Here's one that generates non-singleton lists from any Gen a
:
nonSingleton :: Gen a -> Gen [a]
nonSingleton g = do
x1 <- g
x2 <- g
xs <- listOf g
return $ x1 : x2 : xs
This has the same type as the built-in listOf
function, and can be used in the same way:
useNonSingleton :: Gen Bool
useNonSingleton = do
xs :: [String] <- nonSingleton arbitrary
return $ length xs > 1
Here I've taken advantage of Gen a
being a Monad
so that I could write both the function and the property with do
notation, but you can also write it using monadic combinators if you so prefer.
The function simply generates two values, x1
and x2
, as well as a list xs
of arbitrary size (which can be empty), and creates a list of all three. Since x1
and x2
are guaranteed to be single values, the resulting list will have at least those two values.
Filtering
Sometimes you just want to throw away a small subset of generated values. You can to that with the built-in ==>
combinator, here used directly in a property:
moreThanOne :: (Ord a, Num a) => Positive a -> Property
moreThanOne (Positive i) = i > 1 ==> i > 1
While this property is tautological, it demonstrates that the predicate you place to the left of ==>
ensures that whatever runs on the right-hand side of ==>
has passed the predicate.
Existing monadic combinators
Since Gen a
is a Monad
instance, you can also use existing Monad
, Applicative
, and Functor
combinators. Here's one that turns any number inside of any Functor
into an even number:
evenInt :: (Functor f, Num a) => f a -> f a
evenInt = fmap (* 2)
Notice that this works for any Functor f
, not just for Gen a
. Since, however, Gen a
is a Functor
, you can still use evenInt
:
allIsEven :: Gen Bool
allIsEven = do
i :: Integer <- evenInt arbitrary
return $ even i
The arbitrary
function call here creates an unconstrained Integer
value. evenInt
then makes it even by multiplying it by two.
Arbitrary newtypes
You can also use newtype
to create your own data containers, and then make them Arbitrary
instances:
newtype Odd a = Odd a deriving (Eq, Ord, Show, Read)
instance (Arbitrary a, Num a) => Arbitrary (Odd a) where
arbitrary = do
i <- arbitrary
return $ Odd $ i * 2 + 1
This also enables you to implement shrink
, if you need it.
You can use the newtype
in a property like this:
allIsOdd :: Integral a => Odd a -> Bool
allIsOdd (Odd i) = odd i
The Arbitrary
instance uses arbitrary
for the type a
to generate an unconstrained value i
, then doubles it and adds one, thereby ensuring that the value is odd.
Take a look at the QuickCheck documentation for many more built-in combinators. I particularly find choose
, elements
, oneof
, and suchThat
useful for expressing additional constraints.

- 225,310
- 48
- 427
- 736
-
I really appreciate that you also added examples of how to use these techniques in Properties. Excellent answer, thank you for taking the time. – josiah Jun 05 '19 at 20:58