quickcheck icon indicating copy to clipboard operation
quickcheck copied to clipboard

generic deriving of Arbitrary

Open flip111 opened this issue 8 years ago • 4 comments

Would it be an idea to have a generic implementation of arbitrary in the main QuickCheck library? I saw a package that implements 1 2 a genericArbitrary function. Then there is a generic function for both arbitrary and shrink then the default implementation could be written with these generic functions. People could then just put deriving (Arbitrary) on their types. Possibly a language extensions like DeriveAnyClass should be used for it to work, but i'm not sure about that.

flip111 avatar Dec 16 '16 20:12 flip111

I'm not the maintainer, but I'll chime in anyway:

There currently is a generic implementation of shrink called genericShrink, as you can see here. It is not, however, the default implementation for an emtpy Arbitrary instance. That is, if you just write instance Arbitrary Foo, you'll get shrink _ = [] as the default implementation (see here). You'll have to do this to use genericShrink:

instance Arbitrary Foo where
  shrink = genericShrink

There is currently no genericArbitrary function in QuickCheck. Such a function has been proposed before as the default implementation of arbitrary, but it was rejected.

(FWIW, I'm still lobbying to add genericArbitrary into QuickCheck, just not as the default implementation of arbitrary.)

RyanGlScott avatar Dec 17 '16 21:12 RyanGlScott

This didn't land for QuickCheck-2.10. But in any case, @nick8325, would you be willing to consider adding a standalone genericArbitrary function (not using DefaultSignatures) to the next release? If not, feel free to close this.

RyanGlScott avatar Jun 15 '17 22:06 RyanGlScott

Possibly, but I would have to work out what subset of types I'm comfortable generating automatically. It probably wouldn't include recursive types, but possibly boring old sum-of-product types would be OK. But I'd need to play around with different generators and see what would be a reasonable default.

nick8325 avatar Jun 20 '17 14:06 nick8325

I definitely agree that we should put a prominent warning in the documentation about using this for recursive types. But actually detecting the presence of recursive types proves trickier than you might think. They can crop up in unexpected places - for instance, in this type:

newtype Wrap f a = Wrap (f a)

On the surface, Wrap doesn't appear to be recursive. But if you instantiate f just the right way, e.g., Wrap (Wrap f) f, then it becomes recursive!

(Historical note: GHC.Generics used to have a representation type for recursive types, but they removed it due to its inability to detect everything, as noted above.)

RyanGlScott avatar Jun 20 '17 15:06 RyanGlScott