quickcheck
quickcheck copied to clipboard
discard is too lazy
I expect using discard
in Gen
to discard the current case, but it doesn't discard unless I force it:
-- unexpectedly generates values
sample (discard >>= \_ -> arbitrary :: Gen Int)
-- correctly errors
sample (discard >>= \() -> arbitrary :: Gen Int)
You probably want to use suchThat
or its variant if you are in Gen
, i.e. writing generators.
EDIT: A simple example trying to discard everything with suchThat
will loop, but I doubt that's the real use case.
(It's not discard
which is lazy, it's Gen
monad - and it must be to allow generating infinite structures)
In my specific case, it's a complicated Gen where suchThat wouldnt be easily usable.
Would it be possible to create a version of discard that works even in a lazy Gen monad?
Would it be possible to create a version of discard that works even in a lazy Gen monad?
Not without changing the definition of Gen
, as far as I'm aware.
In my specific case, it's a complicated Gen where suchThat wouldnt be easily usable.
Depends on what you mean by "easily", i.e. below is IMO not too complicated (such definition were proposed in literature, not sure if anyone made a library for it though):
You can work with MaybeT Gen
, then discard
will be return Nothing
, lifting ordinary Gen
into MaybeT Gen
will be lift
, and evaluating the MaybeT Gen
to Gen
would use suchThatMap
.
>>> :t \mg -> suchThatMap (runMaybeT mg) id
\mg -> suchThatMap (runMaybeT mg) id :: MaybeT Gen b -> Gen b
This is the intended behaviour of Gen
as the lazyness allows you to be a bit more fast and loose. One option to make working with discard
slightly more ergonomic would be to introduce suchThatDiscard g p = g >>= \ a -> unless (p a) discard >> pure a