@@ -1,6 +1,14 @@
1−{- |
1+#if __GLASGOW_HASKELL__ >= 806
2+{-# OPTIONS_GHC -Wno-star-is-type #-}
4+#if __GLASGOW_HASKELL__ >= 800
5+{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
3−Generic implementation of the 'arbitrary' method. Example usage:
10+This module is a generic implementation of the 'arbitrary' method. Example
6data Foo = Foo
14data Foo = Foo
@@ -25,45 +33,184 @@ data Foo = Foo
26The generated 'arbitrary' method is equivalent to
34The generated 'arbitrary' method is equivalent to
28−@Foo <$> arbitrary <*> arbitrary@.
37+Foo '<$>' arbitrary '<*>' arbitrary
40+It can also handle a recursive types problem. Assuming a type
50+instance Arbitrary R where
51+ arbitrary = genericArbitrary
52+ shrink = genericShrink
55+If you try to compile this you will get a type level error
57+> • R refers to itself in all constructors
59+Which means that there is no finite term for @R@ because it is recursive. But,
60+if you correct the definition of @R@ like this.
67+Then it will compile. And the @arbitrary@ generated will not hang forever, because
68+it respects the @size@ parameter.
70+There is a limitation of recursion detection:
74+ deriving (Eq, Ord, Show, Generic)
75+ deriving anyclass NFData
76+ deriving Arbitrary via (GenericArbitrary R1)
79+ deriving (Eq, Ord, Show, Generic)
80+ deriving anyclass NFData
81+ deriving Arbitrary via (GenericArbitrary R2)
84+This code will compile and the @arbitrary@ generated will always hang. Yes,
85+there is a problem with mutually recursive types.
87+Now lets see an example of datatype with parameters
91+ deriving (Eq, Ord, Show)
92+ deriving anyclass NFData
95+instance (Arbitrary a) => Arbitrary (A a) where
96+ arbitrary = genericArbitrary
97+ shrink = genericShrink
100+It should work from first glance, but when compile it will throw an error:
103+ • Could not deduce (Test.QuickCheck.Arbitrary.Generic.GArbitrary
106+ ('GHC.Generics.MetaData "A" "ParametersTest" "main" 'False)
108+ ('GHC.Generics.MetaCons "A" 'GHC.Generics.PrefixI 'False)
110+ ('GHC.Generics.MetaSel
112+ 'GHC.Generics.NoSourceUnpackedness
113+ 'GHC.Generics.NoSourceStrictness
114+ 'GHC.Generics.DecidedLazy)
115+ (GHC.Generics.Rec0 a))))
116+ (TypesDiffer (A a) a))
117+ arising from a use of ‘genericArbitrary’
120+Here the @TypesDiffer@ is a type familty dealing with recursive types and
121+helping us to eliminate inproper instances. To convince the compiller, that the
122+@a@ parameter is not an @A a@ we must fix the instance with additional constraint
125+instance (Arg (A a) a, Arbitrary a) => Arbitrary (A a) where
126+ arbitrary = genericArbitrary
127+ shrink = genericShrink
130+Now everything compiles and works as expected.
32module Test.QuickCheck.Arbitrary.Generic
134module Test.QuickCheck.Arbitrary.Generic
137+#if MIN_VERSION_QuickCheck(2, 14, 0)
33− ( GenericArbitrary(..)
138+ , GenericArbitrary(..)
34− , Arbitrary(..)
139+#endif
36− , genericArbitrary
141+ -- * Internal
37 , genericShrink
152 , genericShrink
40import Control.Applicative
155import Control.Applicative
41−import Data.Coerce (coerce)
42import Data.Proxy
156import Data.Proxy
43import Data.Type.Bool
157import Data.Type.Bool
44import GHC.Generics as G
158import GHC.Generics as G
45import GHC.TypeLits
159import GHC.TypeLits
46import Test.QuickCheck as QC
161import Test.QuickCheck as QC
162+#if MIN_VERSION_QuickCheck(2, 14, 0)
163+import Data.Coerce (coerce)
47import Test.QuickCheck.Arbitrary (GSubterms, RecursivelyShrink)
164import Test.QuickCheck.Arbitrary (GSubterms, RecursivelyShrink)
168+-- | Newtype for @DerivingVia@
176+-- } deriving (Generic)
177+-- deriving (Arbitrary) via GenericArbitrary Foo
49newtype GenericArbitrary a = GenericArbitrary { unGenericArbitrary :: a }
181newtype GenericArbitrary a = GenericArbitrary { unGenericArbitrary :: a }
50 deriving (Show, Eq)
182 deriving (Show, Eq)
53 ( Generic a,
185 ( Generic a,
54− GArbitrary a (Rep a) 'True,
186+ GArbitrary a (Rep a) some,
55 RecursivelyShrink (Rep a),
187 RecursivelyShrink (Rep a),
56 GSubterms (Rep a) a
188 GSubterms (Rep a) a
57 ) => Arbitrary (GenericArbitrary a) where
189 ) => Arbitrary (GenericArbitrary a) where
58 arbitrary = coerce (genericArbitrary :: Gen a)
190 arbitrary = coerce (genericArbitrary :: Gen a)
59 shrink = coerce (genericShrink :: a -> [a])
191 shrink = coerce (genericShrink :: a -> [a])
194+-- | Constraint helper for types with parameters
200+-- deriving (Generic)
201+-- instance (Arg (A a) a, Arbitrary a) => Arbitrary (A a) where
202+-- arbitrary = genericArbitrary
203+-- shrink = genericShrink
208+type Arg self field = (TypesDiffer self field ~ 'True)
61type family TypesDiffer a b where
210type family TypesDiffer a b where
62 TypesDiffer a a = 'False
211 TypesDiffer a a = 'False
63 TypesDiffer a b = 'True
212 TypesDiffer a b = 'True
65−type Arg self field = (TypesDiffer self field ~ 'True)
67type family AllFieldsFinal self (a :: * -> *) :: Bool where
214type family AllFieldsFinal self (a :: * -> *) :: Bool where
68 AllFieldsFinal self U1 = 'True
215 AllFieldsFinal self U1 = 'True
69 AllFieldsFinal self (a :*: b) = AllFieldsFinal self a && AllFieldsFinal self b
216 AllFieldsFinal self (a :*: b) = AllFieldsFinal self a && AllFieldsFinal self b
@@ -99,8 +246,8 @@ class (Finite self a ~ finite) => GArbitrary self a (finite :: Bool) where
99 gArbitrary :: Proxy self -> QC.Gen (a x)
246 gArbitrary :: Proxy self -> QC.Gen (a x)
102− ( GArbitrary self (C1 c f) 'True
249+ ( GArbitrary self (M1 C c f) 'True
103− ) => GArbitrary self (D1 t (C1 c f)) 'True where
250+ ) => GArbitrary self (M1 D t (M1 C c f)) 'True where
104 gArbitrary _ = M1 <$> gArbitrary (Proxy :: Proxy self)
251 gArbitrary _ = M1 <$> gArbitrary (Proxy :: Proxy self)
106-- | The constructor meta information
253-- | The constructor meta information
@@ -108,7 +255,7 @@ instance
108 ( GArbitrary self f some
255 ( GArbitrary self f some
109 , KnownNat (ArgumentsCount f)
256 , KnownNat (ArgumentsCount f)
110 , AllFieldsFinal self f ~ some
257 , AllFieldsFinal self f ~ some
111− ) => GArbitrary self (C1 c f) some where
258+ ) => GArbitrary self (M1 C c f) some where
112 gArbitrary _ = M1 <$> scale predNat (gArbitrary (Proxy :: Proxy self))
259 gArbitrary _ = M1 <$> scale predNat (gArbitrary (Proxy :: Proxy self))
114 argumentsCount = fromIntegral $ natVal (Proxy :: Proxy (ArgumentsCount f))
261 argumentsCount = fromIntegral $ natVal (Proxy :: Proxy (ArgumentsCount f))
@@ -121,14 +268,14 @@ instance GArbitrary self U1 'True where
121 gArbitrary _ = pure U1
268 gArbitrary _ = pure U1
123-- | Constructor field meta information
270-- | Constructor field meta information
124−instance GArbitrary self f some => GArbitrary self (S1 t f) some where
271+instance GArbitrary self f some => GArbitrary self (M1 S t f) some where
125 gArbitrary _ = M1 <$> gArbitrary (Proxy :: Proxy self)
272 gArbitrary _ = M1 <$> gArbitrary (Proxy :: Proxy self)
127-- | Data of the constructor field
274-- | Data of the constructor field
129 ( Arbitrary t
276 ( Arbitrary t
130− , Finite self (Rec0 t) ~ some
277+ , Finite self (K1 R t) ~ some
131− ) => GArbitrary self (Rec0 t) some where
278+ ) => GArbitrary self (K1 R t) some where
132 gArbitrary _ = K1 <$> arbitrary
279 gArbitrary _ = K1 <$> arbitrary
134-- | Product
281-- | Product
@@ -144,7 +291,7 @@ instance
145 ( TypeError (ShowType self :<>: Text " refers to itself in all constructors")
292 ( TypeError (ShowType self :<>: Text " refers to itself in all constructors")
146 , AllFieldsFinal self f ~ 'False
293 , AllFieldsFinal self f ~ 'False
147− ) => GArbitrary self (D1 t (C1 c f)) 'False where
294+ ) => GArbitrary self (M1 D t (M1 C c f)) 'False where
148 gArbitrary _ = error "Unreachable"
295 gArbitrary _ = error "Unreachable"
@@ -152,7 +299,7 @@ instance
153 ( FiniteSum self a b af bf
300 ( FiniteSum self a b af bf
154 , GArbitrary self (a :+: b) 'True
301 , GArbitrary self (a :+: b) 'True
155− ) => GArbitrary self (D1 t (a :+: b)) 'True where
302+ ) => GArbitrary self (M1 D t (a :+: b)) 'True where
156 gArbitrary _ = sized $ \s -> M1 <$>
303 gArbitrary _ = sized $ \s -> M1 <$>
158 then gArbitrary (Proxy :: Proxy self)
305 then gArbitrary (Proxy :: Proxy self)
@@ -208,8 +355,8 @@ instance
208 finiteElem _ = finiteSum (Proxy :: Proxy self)
355 finiteElem _ = finiteSum (Proxy :: Proxy self)
211− ( GArbitrary self (C1 c f) 'True
358+ ( GArbitrary self (M1 C c f) 'True
212− ) => FiniteSumElem self (C1 c f) where
359+ ) => FiniteSumElem self (M1 C c f) where
213 finiteElem _ = [gArbitrary (Proxy :: Proxy self)]
360 finiteElem _ = [gArbitrary (Proxy :: Proxy self)]
@@ -217,7 +364,7 @@ instance
218 ( TypeError (ShowType self :<>: Text " refers to itself in all constructors")
365 ( TypeError (ShowType self :<>: Text " refers to itself in all constructors")
219 , (Finite self a || Finite self b) ~ 'False
366 , (Finite self a || Finite self b) ~ 'False
220− ) => GArbitrary self (D1 t (a :+: b)) 'False where
367+ ) => GArbitrary self (M1 D t (a :+: b)) 'False where
221 gArbitrary _ = error "Unreachable"
368 gArbitrary _ = error "Unreachable"