@@ -1,14 +1,31 @@
1# generic-arbitrary
1# generic-arbitrary
3−[](https://travis-ci.org/typeable/generic-arbitrary)
3+[](https://github.com/typeable/generic-arbitrary/actions)
5−Deriving `Arbitrary` via `Generic`.
7+Package for deriving `Arbitrary` via `Generic`.
8import GHC.Generics (Generic)
10import GHC.Generics (Generic)
9import Test.QuickCheck
11import Test.QuickCheck
10import Test.QuickCheck.Arbitrary.Generic
12import 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
14 | Add Expr Expr
31 | Add Expr Expr
@@ -20,19 +37,101 @@ instance Arbitrary Expr where
20 shrink = genericShrink
37 shrink = genericShrink
23−if type has an argument then `Arg` is required
40+Which is generally the same.
42+# Infinite terms problem
44+The `generic-arbitrary` can partially handle the problem with recursive
45+types. Assume the type `R`
25``` haskell
47``` haskell
26−data Expr lit
48+data R = R R
30− deriving (Eq, Show, Generic)
49+ deriving Generic
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
32−instance
64+Which means that there is no finite term for `R` because it is recursive in all
33− ( Arbitrary lit
65+it's constructors. But, if you correct the definition of `R` like this.
34− , Arg (Expr lit) lit
66+
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:
81+ deriving (Eq, Ord, Show, Generic)
82+ deriving anyclass NFData
83+ deriving Arbitrary via (GenericArbitrary R1)
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 lets see an example of datatype with parameters
100+ deriving (Eq, Ord, Show)
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:
111+> • Could not deduce (Test.QuickCheck.Arbitrary.Generic.GArbitrary
114+> ('GHC.Generics.MetaData "A" "ParametersTest" "main" 'False)
116+> ('GHC.Generics.MetaCons "A" 'GHC.Generics.PrefixI 'False)
118+> ('GHC.Generics.MetaSel
120+> 'GHC.Generics.NoSourceUnpackedness
121+> 'GHC.Generics.NoSourceStrictness
122+> 'GHC.Generics.DecidedLazy)
123+> (GHC.Generics.Rec0 a))))
124+> (TypesDiffer (A a) a))
125+> arising from a use of ‘genericArbitrary’
127+Here the `TypesDiffer` is a type familty dealing with recursive types and
128+helping us to eliminate inproper instances. To convince the compiller, that the
129+`a` parameter is not an `A a` we must fix the instance with additional constraint
35− ) => Arbitrary (Expr lit) where
132+instance (Arg (A a) a, Arbitrary a) => Arbitrary (A a) where
36 arbitrary = genericArbitrary
133 arbitrary = genericArbitrary
37 shrink = genericShrink
134 shrink = genericShrink
137+Now everything compiles and works as expected.