Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a lists of a specific length with Haskell's QuickCheck

-- 3 (find k"th element of a list)
element_at xs x = xs !! x
prop_3a xs x = (x < length xs && x >= 0) ==> element_at xs (x::Int) == (xs !! x::Int)

When prop_3a is ran through QuickCheck, it gives up, because it won't generate long enough lists.

How can I write a generator that will generate lists with length longer than the random integer?

like image 402
Joe Van Dyk Avatar asked Oct 08 '11 00:10

Joe Van Dyk


People also ask

What is QuickCheck in Haskell?

QuickCheck is a software library, specifically a combinator library, originally written in the programming language Haskell, designed to assist in software testing by generating test cases for test suites – an approach known as property testing.

How do I import a QuickCheck into Ghci?

If you want to use QuickCheck in a command such as stack ghci or stack ghc , you can add it as a --package option e.g. to run a REPL to play around with QuickCheck you can use stack ghci --package QuickCheck and then write import Test. QuickCheck .

Is Haskell QuickCheck static?

Haskell is a statically-typed functional programming language: Haskell type declarations are identified and checked during compilation, guaranteeing that program semantics are known by the designers before execution or possibly even before the implementation.


1 Answers

hammar's answer is perfectly adequate for the problem. But for the sake of answering the precise question asked, I couldn't help but investigate a bit. Let's use forAll.

prop_bang x = x >= 0 ==> forAll (listLongerThan x) $ \xs ->
  element_at xs x == xs !! x

So now we need a function, listLongerThan :: Int -> Gen [Int]. It takes a length, x, and produces a generator which will produce lists of length greater than x.

listLongerThan :: Int -> Gen [Int]
listLongerThan x = replicateM (x+1) arbitrary

It's rather straightforward: we simply take advantage of the Monad instance of Gen. If you run quickCheck prop_bang, you'll notice it starts taking quite a long time, because it begins testing absurdly long lists. Let's limit the length of the list, to make it go a bit faster. Also, right now listLongerThan only generates a list that is exactly x+1 long; let's mix that up a bit, again utilizing the Monad instance of Gen.

prop_bang =
  forAll smallNumber $ \x ->
  forAll (listLongerThan x) $ \xs ->
  element_at xs x == xs !! x

smallNumber :: Gen Int
smallNumber = fmap ((`mod` 100) . abs) arbitrary

listLongerThan :: Int -> Gen [Int]
listLongerThan x = do
  y <- fmap (+1) smallNumber -- y > 0
  replicateM (x+y) arbitrary

You can use sample smallNumber or sample (listLongerThan 3) in ghci to make sure it is generating the correct stuff.

like image 181
Dan Burton Avatar answered Oct 17 '22 19:10

Dan Burton