Package for deriving
import GHC.Generics (Generic) import Test.QuickCheck import Test.QuickCheck.Arbitrary.Generic data Expr = Lit Int | Add Expr Expr | Mul Expr Expr deriving (Eq, Show, Generic) deriving Arbitrary via (GenericArbitrary Expr)
Older versions of this package had a problem with hanging
1.0.0 this problem almost solved.
QuickCheck older than
GenericArbitrary is not available, so
you will need to write instances more verbosely
data Expr = Lit Int | Add Expr Expr | Mul Expr Expr deriving (Eq, Show, Generic) instance Arbitrary Expr where arbitrary = genericArbitrary shrink = genericShrink
Which is generally the same.
Infinite terms problem
generic-arbitrary can partially handle the problem with recursive
types. Assume the type
data R = R R deriving Generic
there is no instance
instance Arbitrary R where arbitrary = genericArbitrary shrink = genericShrink
If you try to compile this you will get a type level error
• R refers to itself in all constructors
Which means that there is no finite term for
R because it is recursive in all
it’s constructors. But, if you correct the definition of
R like this.
data R = R R | F deriving Generic
Then it will compile. And the
arbitrary generated will not hang forever,
because it respects the
There is a limitation of recursion detection:
data R1 = R1 R2 deriving (Eq, Ord, Show, Generic) deriving anyclass NFData deriving Arbitrary via (GenericArbitrary R1) data R2 = R2 R1 deriving (Eq, Ord, Show, Generic) deriving anyclass NFData deriving Arbitrary via (GenericArbitrary R2)
This code will compile and the
arbitrary generated will always hang. Yes,
there is a problem with mutually recursive types.
Now let’s see an example of datatype with parameters
data A a = A a deriving (Eq, Ord, Show) deriving anyclass NFData deriving (Generic) instance (Arbitrary a) => Arbitrary (A a) where arbitrary = genericArbitrary shrink = genericShrink
It should work from first glance, but when compile it will throw an error:
• Could not deduce (Test.QuickCheck.Arbitrary.Generic.GArbitrary (A a) (GHC.Generics.D1 ('GHC.Generics.MetaData "A" "ParametersTest" "main" 'False) (GHC.Generics.C1 ('GHC.Generics.MetaCons "A" 'GHC.Generics.PrefixI 'False) (GHC.Generics.S1 ('GHC.Generics.MetaSel 'Nothing 'GHC.Generics.NoSourceUnpackedness 'GHC.Generics.NoSourceStrictness 'GHC.Generics.DecidedLazy) (GHC.Generics.Rec0 a)))) (TypesDiffer (A a) a)) arising from a use of ‘genericArbitrary’
TypesDiffer is a type familty dealing with recursive types and
helping us to eliminate inproper instances. To convince the compiller, that the
a parameter is not an
A a we must fix the instance with additional
Arg (A a) a
instance (Arg (A a) a, Arbitrary a) => Arbitrary (A a) where arbitrary = genericArbitrary shrink = genericShrink
Now everything compiles and works as expected.
- Compillability by GHC-9.2.4
- Fix cabal
- Fixed issue with too big terms in case of recursive types
- Recpect the
- Types with parameters require
- Resolved an issue where the size of the generators could become negative
- Added compatibility with GHC 9.2.1
GenericArbitraryfor use with the DerivingVia compiler extension.
- Minimum bound on QuickCheck changed to 2.14.
- Public release