@@ -1,31 +1,14 @@
1# generic-arbitrary
1# generic-arbitrary
3−[](https://github.com/typeable/generic-arbitrary/actions/workflows/haskell-ci.yml)
3+[](https://travis-ci.org/typeable/generic-arbitrary)
7−Package for deriving `Arbitrary` via `Generic`.
5+Deriving `Arbitrary` via `Generic`.
10import GHC.Generics (Generic)
8import GHC.Generics (Generic)
11import Test.QuickCheck
9import Test.QuickCheck
12import Test.QuickCheck.Arbitrary.Generic
10import Test.QuickCheck.Arbitrary.Generic
18− deriving (Eq, Show, Generic)
19− deriving Arbitrary via (GenericArbitrary Expr)
22−Older versions of this package had a problem with hanging `arbitrary`
23−method. Since `1.0.0` this problem almost solved.
25−For `QuickCheck` older than `2.14.0` the `GenericArbitrary` is not available, so
26−you will need to write instances more verbosely
31 | Add Expr Expr
14 | Add Expr Expr
@@ -37,104 +20,19 @@ instance Arbitrary Expr where
37 shrink = genericShrink
20 shrink = genericShrink
40−Which is generally the same.
23+if type has an argument then `Arg` is required
42−# Infinite terms problem
44−The `generic-arbitrary` can partially handle the problem with recursive
45−types. Assume the type `R`
55−instance Arbitrary R where
56− arbitrary = genericArbitrary
57− shrink = genericShrink
60−If you try to compile this you will get a type level error
62−> • R refers to itself in all constructors
64−Which means that there is no finite term for `R` because it is recursive in all
65−it's constructors. But, if you correct the definition of `R` like this.
72−Then it will compile. And the `arbitrary` generated will not hang forever,
73−because it respects the `size` parameter.
77−There is a limitation of recursion detection:
79``` haskell
25``` haskell
80−data R1 = R1 R2
26+data Expr lit
81− deriving (Eq, Ord, Show, Generic)
27+ = Lit lit
82− deriving anyclass NFData
28+ | Add Expr Expr
83− deriving Arbitrary via (GenericArbitrary R1)
29+ | Mul Expr Expr
86− deriving (Eq, Ord, Show, Generic)
87− deriving anyclass NFData
88− deriving Arbitrary via (GenericArbitrary R2)
91−This code will compile and the `arbitrary` generated will always hang. Yes,
92−there is a problem with mutually recursive types.
96−Now let's see an example of datatype with parameters
100− deriving (Eq, Ord, Show)
30+ deriving (Eq, Show, Generic)
101− deriving anyclass NFData
104−instance (Arbitrary a) => Arbitrary (A a) where
105− arbitrary = genericArbitrary
106− shrink = genericShrink
109−It should work from first glance, but when compile it will throw an error:
112−• Could not deduce (Test.QuickCheck.Arbitrary.Generic.GArbitrary
115− ('GHC.Generics.MetaData "A" "ParametersTest" "main" 'False)
117− ('GHC.Generics.MetaCons "A" 'GHC.Generics.PrefixI 'False)
119− ('GHC.Generics.MetaSel
121− 'GHC.Generics.NoSourceUnpackedness
122− 'GHC.Generics.NoSourceStrictness
123− 'GHC.Generics.DecidedLazy)
124− (GHC.Generics.Rec0 a))))
125− (TypesDiffer (A a) a))
126− arising from a use of ‘genericArbitrary’
129−Here the `TypesDiffer` is a type familty dealing with recursive types and
130−helping us to eliminate inproper instances. To convince the compiller, that the
131−`a` parameter is not an `A a` we must fix the instance with additional
132−constraint `Arg (A a) a`
134−``` haskell
32+instance
135−instance (Arg (A a) a, Arbitrary a) => Arbitrary (A a) where
35+ ) => Arbitrary (Expr lit) where
136 arbitrary = genericArbitrary
36 arbitrary = genericArbitrary
137 shrink = genericShrink
37 shrink = genericShrink
140−Now everything compiles and works as expected.