Øystein Kolsrud
Øystein Kolsrud

Reputation: 389

QuickCheck with Dynamic Element Sets

Is there a way to control programmatically the set of values to use in an elements call within an arbitrary definition? I want to be able to generate an arbitrary variable reference as part of a random expression, but the set of variables identifiers to choose from should be configurable.

As example, imagine the following data type:

data Expr = Num   Int
          | Var   String
          | BinOp Op Expr Expr

data Op = Add | Sub | Mul | Div deriving (Eq, Ord, Enum)

And then I want to define an arbitrary instance for this type that would look something like this:

instance Arbitrary Op where
  arbitrary = elements [Add .. ]

instance Arbitrary Expr where
  arbitrary = oneof [ fmap Num arbitrary
                    , arbitraryBinOp
                    , fmap Var (elements varNames)
                    ]

arbitraryBinOp = do (op, e0, e1) <- arbitrary
                    return (BinOp op e0 e1)

Now the tricky thing is the "varNames" part. Conceptually I would like to be able to do something like this:

do args  <- getArgs
   tests <- generate $ vectorOf 10 ((arbitrary args)::Gen Expr)

But obviously I can't propagate that args-vector down through the arbitrary calls as "arbitrary" does not take such an argument...

Upvotes: 1

Views: 166

Answers (1)

Li-yao Xia
Li-yao Xia

Reputation: 33399

Arbitrary is really only a convenience when the generator does not require any context. If you need to parameterize your generators, you can define them as regular functions, and QuickCheck has combinators to use such explicit generators instead of Arbitrary instances.

genExpr :: [String] -> Gen Expr
genExpr varNames =
    oneof [ fmap Num arbitrary
          , arbitraryBinOp
          , fmap Var (elements varNames)
          ]

main :: IO ()
main = do
    args <- getArgs
    tests <- generate $ vectorOf 10 (genExpr args)
    {- do stuff -}
    return ()

Upvotes: 3

Related Questions