@@ -1,31 +1,14 @@
11# generic-arbitrary
22
3−[](https://github.com/typeable/generic-arbitrary/actions/workflows/haskell-ci.yml)
3+[](https://travis-ci.org/typeable/generic-arbitrary)
44
5−# What?
6−
7−Package for deriving `Arbitrary` via `Generic`.
5+Deriving `Arbitrary` via `Generic`.
86
97``` haskell
108import GHC.Generics (Generic)
119import Test.QuickCheck
1210import Test.QuickCheck.Arbitrary.Generic
1311
14−data Expr
15− = Lit Int
16− | Add Expr Expr
17− | Mul Expr Expr
18− deriving (Eq, Show, Generic)
19− deriving Arbitrary via (GenericArbitrary Expr)
20−```
21−
22−Older versions of this package had a problem with hanging `arbitrary`
23−method. Since `1.0.0` this problem almost solved.
24−
25−For `QuickCheck` older than `2.14.0` the `GenericArbitrary` is not available, so
26−you will need to write instances more verbosely
27−
28−``` haskell
2912data Expr
3013 = Lit Int
3114 | Add Expr Expr
@@ -37,104 +20,19 @@ instance Arbitrary Expr where
3720 shrink = genericShrink
3821```
3922
40−Which is generally the same.
41−
42−# Infinite terms problem
43−
44−The `generic-arbitrary` can partially handle the problem with recursive
45−types. Assume the type `R`
46−
47−``` haskell
48−data R = R R
49− deriving Generic
50−```
51−
52−there is no instance
53−
54−``` haskell
55−instance Arbitrary R where
56− arbitrary = genericArbitrary
57− shrink = genericShrink
58−```
59−
60−If you try to compile this you will get a type level error
61−
62−> • R refers to itself in all constructors
63−
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.
66−
67−``` haskell
68−data R = R R | F
69− deriving Generic
70−```
71−
72−Then it will compile. And the `arbitrary` generated will not hang forever,
73−because it respects the `size` parameter.
74−
75−## Limitation
76−
77−There is a limitation of recursion detection:
23+if type has an argument then `Arg` is required
7824
7925``` haskell
80−data R1 = R1 R2
81− deriving (Eq, Ord, Show, Generic)
82− deriving anyclass NFData
83− deriving Arbitrary via (GenericArbitrary R1)
84−
85−data R2 = R2 R1
86− deriving (Eq, Ord, Show, Generic)
87− deriving anyclass NFData
88− deriving Arbitrary via (GenericArbitrary R2)
89−```
90−
91−This code will compile and the `arbitrary` generated will always hang. Yes,
92−there is a problem with mutually recursive types.
93−
94−# Type parameters
95−
96−Now let's see an example of datatype with parameters
97−
98−``` haskell
99−data A a = A a
100− deriving (Eq, Ord, Show)
101− deriving anyclass NFData
102− deriving (Generic)
103−
104−instance (Arbitrary a) => Arbitrary (A a) where
105− arbitrary = genericArbitrary
106− shrink = genericShrink
107−```
108−
109−It should work from first glance, but when compile it will throw an error:
110−
111−```
112−• Could not deduce (Test.QuickCheck.Arbitrary.Generic.GArbitrary
113− (A a)
114− (GHC.Generics.D1
115− ('GHC.Generics.MetaData "A" "ParametersTest" "main" 'False)
116− (GHC.Generics.C1
117− ('GHC.Generics.MetaCons "A" 'GHC.Generics.PrefixI 'False)
118− (GHC.Generics.S1
119− ('GHC.Generics.MetaSel
120− 'Nothing
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’
127−```
128−
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`
26+data Expr lit
27+ = Lit lit
28+ | Add Expr Expr
29+ | Mul Expr Expr
30+ deriving (Eq, Show, Generic)
13331
134−``` haskell
135−instance (Arg (A a) a, Arbitrary a) => Arbitrary (A a) where
32+instance
33+ ( Arbitrary lit
34+ , Arg (Expr lit) lit
35+ ) => Arbitrary (Expr lit) where
13636 arbitrary = genericArbitrary
13737 shrink = genericShrink
13838```
139−
140−Now everything compiles and works as expected.