When developing a Haskell library we often have a test-suite as part of the package. That test-suite will often have some
orphan instances of Arbitrary
and CoArbitrary
instances from QuickCheck
for custom data types declared within
the library. Which is OK, if no one else will ever need those instances, but in case when the library in question is
highly reusable by others, it will be very likely that others could benefit from having those instances available to them as
well. Unfortunately it is impossible to import those instances from the test-suite, but including them in the library
itself isn't practical either, since that would include an unnecessary dependency on QuickCheck
.
The common solution to this problem is to have a separate package that includes just those orphan instances and depends
both on the QuickCheck and the library, which is in itself becomes a dependency of the test-suite. This works, but produces
an unnecessary, in my opinion, overhead of maintaining an extra package and clutters hackage with packages with orphan
instances, that are useful only for testing. Same pattern seems to emerge for validity
, genvalidity
packages and
possibly others that are used for testing.
I think, there is no reason to have those instances be included as orphans in a separate package and they can be
placed right where they belong, next to the declaration of data types. All we have to do is use a custom cabal flag,
quickcheck
in the example above, but it could be anything, eg. test-mode
could be a logical name as well.
Once it's implemented in the way presented above we will have main library depending on QuickCheck
only when it is
being used from within a test suite:
$ stack build
arbitrary-0.1.0.0: configure (lib)
Configuring arbitrary-0.1.0.0...
arbitrary-0.1.0.0: build (lib)
Preprocessing library for arbitrary-0.1.0.0..
Building library for arbitrary-0.1.0.0..
[1 of 1] Compiling Lib ( src/Lib.hs, .stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/Lib.o )
arbitrary-0.1.0.0: copy/register
Installing library in /home/user/arbitrary/.stack-work/install/x86_64-linux/lts-11.14/8.2.2/lib/x86_64-linux-ghc-8.2.2/arbitrary-0.1.0.0-ImHjisNdlc5EoXoKrW90uk
Registering library for arbitrary-0.1.0.0..
$
$ stack test --flag='arbitrary:quickcheck'
arbitrary-0.1.0.0: unregistering (missing dependencies: QuickCheck)
primitive-0.6.4.0: using precompiled package
random-1.1: using precompiled package
Progress 0/6ghc-pkg: cannot find package primitive-0.6.4.0
ghc-pkg: cannot find package random-1.1
tf-random-0.5: using precompiled package
Progress 2/6: tf-random-0.5ghc-pkg: cannot find package tf-random-0.5
QuickCheck-2.10.1: using precompiled package
Progress 3/6: QuickCheck-2.10.1ghc-pkg: cannot find package QuickCheck-2.10.1
arbitrary-0.1.0.0: configure (lib + test)
Configuring arbitrary-0.1.0.0...
arbitrary-0.1.0.0: build (lib + test)
Preprocessing library for arbitrary-0.1.0.0..
Building library for arbitrary-0.1.0.0..
[1 of 1] Compiling Lib ( src/Lib.hs, .stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/Lib.o )
Preprocessing test suite 'arbitrary-test' for arbitrary-0.1.0.0..
Building test suite 'arbitrary-test' for arbitrary-0.1.0.0..
[1 of 1] Compiling Main ( test/Spec.hs, .stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/arbitrary-test/arbitrary-test-tmp/Main.o )
Linking .stack-work/dist/x86_64-linux/Cabal-2.0.1.0/build/arbitrary-test/arbitrary-test ...
arbitrary-0.1.0.0: copy/register
Installing library in /home/user/arbitrary/.stack-work/install/x86_64-linux/lts-11.14/8.2.2/lib/x86_64-linux-ghc-8.2.2/arbitrary-0.1.0.0-FnF56kbnAsXLPpyQ6XsKsW
Registering library for arbitrary-0.1.0.0..
arbitrary-0.1.0.0: test (suite: arbitrary-test)
Progress 5/6: arbitrary-0.1.0.0+++ OK, passed 100 tests.
arbitrary-0.1.0.0: Test suite arbitrary-test passed
Completed 6 action(s).