Hi, I wonder if you have considered using a technique like https://code.google.com/p/fsharp-typeclasses/ to implement typeclasses in FsCheck.
I know this would be a pretty big change, perhaps not worth the effort. But it's definitely a closer approximation to Haskell's typeclasses (e.g. type-safe, no reflection).
So basically, just trying to pick your brain about the potential pros and cons of this hypothetical change :)
For example, it would seem to make it harder to infer generators via reflection for discriminated unions.
Dec 7, 2012 at 11:55 AM
Yes, in fact I have! There are two main uses of typeclasses in FsCheck. For one of them, I have considered replacing the current (runtime) implementation with an implementation at compile time. For the other one, I don't think it makes sense.
Use 1: map from type to a default generator for the type. I think using a static overloading mechanism here is not idiomatic, as it takes useful functionality away - the ability to change default generators for types at runtime. Two important use cases are
- (1) specifying a number of generators scoped to a bunch of test methods, and (2) overriding a particular generator while keeping the default generator for the aggregate type. Say you have a record type (int, string, string, double), and you are fine with
the default int and double generator but don't want empty strings. You can then just override the string generator, and keep using the default (reflection-based) generator for the record type. With static overloading you'd have to rewrite the record type generator,
which is pretty tedious. Also you end up wrapping primitive types a lot to take care of special cases (NonEmptyString, NonZeroDouble, etc).
To sum up, I think making the whole thing dynamic has some benefits that outweigh the cost (i.e. no type safety so occasionally annoying errors) . I have tried to remedy the latter a bit by having lots of debug output when you override generators
(it says what generators it adds, which ones it overrides, etc).
Use 2: map from result type of a test to a Testable (e.g. for unit the test fails it doesn't thrown an exception, for boolean if it returns true, etc). At one point I started refactoring that to the approach you mentioned, but as I remember I ran into ordering
issues, it became unwieldy, and compilation became pretty slow pretty quickly. I am unsure if the latter would carry over to FsCheck users (I suspect not). In the end I gave up somewhere halfway through because since this use case is effectively completely
contained inside FsCheck, it doesn't bring much to the table for users. It would however get rid of some initialization problems that can potentially still happen.
It's an interesting technique.
Hope that makes sense!