0

For using QuickCheck, I'm trying to implement the Arbitrary instance:

import Test.QuickCheck
import Test.QuickCheck.Arbitrary (class Arbitrary, class Coarbitrary)

data Arith = Lit Int | Add Arith Arith | Neg Arith

derive instance arithEq :: Eq Arith

instance arbitraryFoo :: Arbitrary Arith where
    arbitrary = arbLit <|> arbAdd <|> arbNeg where
        arbAdd = do
            a <- arbitrary
            b <- arbitrary
            pure $ Add a b
        arbNeg = do
            a <- arbitrary
            pure $ Neg a
        arbLit = do
            x <- arbitrary
            pure $ Lit x

But PureScript told me that

The value of arbitraryFoo is undefined here, so this reference is not allowed.

And when I change my code acroding to the guide from the error message:

import Test.QuickCheck
import Test.QuickCheck.Arbitrary (class Arbitrary, class Coarbitrary)
import Control.Lazy

instance arbitraryFoo :: Arbitrary Arith where
    arbitrary = fix $ \arb -> let
        arbAdd = do
            a <- arb
            b <- arb
            pure $ Add a b
        arbNeg = do
            a <- arb
            pure $ Neg a
        arbLit = do
            x <- arbitrary
            pure $ Lit x
        in arbLit <|> arbAdd <|> arbNeg

I still got some error:

RangeError: Maximum call stack size exceeded

So, how can I fix that? and why Arbitrary can not be derived automatically?

luochen1990
  • 3,689
  • 1
  • 22
  • 37

1 Answers1

0

To prevent it from generating an infinitely recursive structure, you should make the generator sized, and then resize by -1 each time it is used recursively.

Here's an example of a JSON generator using that technique: https://github.com/purescript-contrib/purescript-argonaut-core/blob/37fc57c722851b1936c438db750aa8195dc630c7/src/Data/Argonaut/Gen.purs. Although the Gen is abstracted with MonadGen, the same principle applies if you want to implement an Arbitrary.

gb.
  • 4,629
  • 1
  • 20
  • 19