QuickCheck generator of (List, Index) where index within list range

As of March 2020, School of Haskell has been switched to read-only mode.

(List,Index) pair generator keeping the index within range

class Arbitrary a where

  arbitrary :: Gen a

  shrink :: a -> [a]

QuickCheck Gen is a monad.

Here is the Arbitrary instance for lists, taken from the QuickCheck source

instance Arbitrary a => Arbitrary [a] where
  arbitrary = sized $ \n ->
    do k <- choose (0,n)       -- list length
       sequence [ arbitrary | _ <- [1..k] ] -- :: m [a]

  shrink xs = shrinkList shrink xs

Let's add an index generator within [0, length list) except for empty lists giving 0 as index.


data ListWithIdx a = ListWithIdx [a] Int deriving (Eq, Show)

instance Arbitrary a => Arbitrary (ListWithIdx a) where

  arbitrary = sized $ \n -> do
       k <- choose (0, n) -- list length
       -- added index
       i <- if k == 0 then return 0 else choose (0, k-1)
       
       list <- sequence [ arbitrary | _ <- [1..k] ]
       -- wrap them in the new type
       return $ ListWithIdx list i

Adding a shrink function to the instance: shrink the list and lessen the index according to lengths difference


  shrink (ListWithIdx xs i) = 
  
     map wrap $ shrinkList shrink xs
      where
        lenXs = length xs
        
        wrap ys = assert (lenYs <= lenXs) $ 
        
                    ListWithIdx ys $ max 0 $ i - (lenXs - lenYs)
          where
            lenYs = length ys

Test it:

import Data.List as L
import Test.QuickCheck.Gen
import Test.QuickCheck as Q
import Control.Exception (assert)

data ListWithIdx a = ListWithIdx [a] Int deriving (Eq, Show)

-- include Arbitrary instance for ListWithIdx here       
-- with arbitrary and shrink methods

propIndexIsInRange :: [ListWithIdx Int] -> Bool
propIndexIsInRange xs = all test xs
  where
    test (ListWithIdx list idx) = if null list
                                      then idx == 0
                                      else idx >= 0 && idx < length list
main :: IO ()
main = quickCheckWith Q.stdArgs {Q.maxSuccess = 30, Q.maxSize = 22} 
                      propIndexIsInRange