How to generate a non-empty list of items with a predicate (using suchThat)?
runeksvendsen opened this issue · 2 comments
I've copied suchThat
: https://github.com/feuerbach/smallcheck/blob/8630db2a59aa942a4ac25fb42b6f957c1fb1fca3/Test/SmallCheck/Series.hs#L231
into my own codebase, since it isn't exported by SmallCheck, but is really useful.
I'm having some problems using it to generate a non-empty list, however, since I end up with really inefficient code:
import Test.SmallCheck.Series
import qualified Test.SmallCheck.Series as SS
instance Serial m (OrderBook venue base quote) where
series = do
midPrice <- series
let buyOrderProp o = oPrice (buyOrder o) < midPrice
sellOrderProp o = oPrice (sellOrder o) > midPrice
SS.NonEmpty buyOrders <- series `suchThat` (all buyOrderProp . SS.getNonEmpty)
SS.NonEmpty sellOrders <- series `suchThat` (all sellOrderProp . SS.getNonEmpty)
return $ OrderBook (Vec.fromList $ sort buyOrders)
(Vec.fromList $ sort sellOrders)
When depth gets greater than 5, tests grind to a halt since any list which contains just a single item that doesn't fulfill the predicate is discarded.
Is there a way to use suchThat
in combination with NonEmpty
to generate a non-empty list of items that all adhere to a predicate on a per-item basis (as opposed to applying suchThat
to the entire list)?
Side note: when using the above series
implementation, SmallCheck fails to find counterexamples at depth 5, even though it successfully finds counterexamples at the same depth when using a more performant series
implementation (which uses Test.SmallCheck.Series.list
in combination with suchThat
(although this doesn't work for non-empty lists)).
Sorry, I won't be able to help you — I just don't have the time. I suggest you come up with a working example and ask on stackoverflow or a similar venue.
For the record, I found a solution that performs better, which is simply:
nonEmptyList :: Series Identity a -> Series m [a]
nonEmptyList series = do
depth <- getDepth
return (depth `SS.list` series) `suchThat` (not . null)
and then you can apply your predicate to the individual items by using suchThat
on the Series Identity a
passed to nonEmptyList
.