[PROPOSAL] Adding Generics-based DefaultSignature to `deepseq` package

Herbert Valerio Riedel hvr at gnu.org
Thu Oct 16 10:39:58 UTC 2014


The Proposal
============

I hereby propose to merge `deepseq-generics`[2] into `deepseq`[1] in
order to add Generics support to the `NFData` class based on the
`-XDeriveGenerics` and `-XDefaultSignature` language extensions.

A concrete patch is available for bike-review at [3]


Prior Proposal & What's changed
===============================

About 2 years ago, I already proposed something similar[4].  Back then
the major concern was avoiding a conditionally exported API as using the
(back then) rather young `Generics` extension would leave the Haskell98
domain.

This lead to me release Generics support as a companion package[2] which
turns out to have become a rather popular package (judging from the
Hackage download-count stats).

I only realized after the discussion was effectively finished, that
having a separate `deepseq-generics` actually does have an IMO
non-neglectable downside:

  You can't support a `DefaultSignature`-based default implementation,
as those need to be backed into the `NFData` class.

Missing out on `DefaultSignature` would be a shame IMO, because

 * There's a chance that starting with GHC 7.10 `deriving` may work for
   arbitrary classes[5], putting `NFData` on equal footing as built-in
   classes such as `Eq` or `Show`. Specifically, you would be able to
   write
  
      data Foo = Foo [Int] String (Bool,Char) | Bar (Maybe Char)
                 deriving (Show, Generic, NFData)

   instead of having to manually write the following boilerplate

      instance NFData Foo where
         rnf (Foo x y z) = rnf x `seq` rnf y `seq` rnf z
         rnf (Bar x)     = rnf x

   which gets tedious rather soon if you have many (and more complex)
   types and tend to refactor regularly (with a risk of failing to adapt
   your manual instances if you change the strictness of fields)


 * The current default `rnf` implementation, i.e.

     rnf a = a `seq` ()

   is rather error-prone, as it's *very* easy to end up with an
   incorrect instance. Especially after refactoring a type for which the
   NF=WHNF assumption was broken after refactoring by adding new fields,
   or changing the strictness of existing fields.

   The Generics-derived `rnf` implementation does not have such a
   problem.


Moreover, popular packages are starting adopt (and even recommend) the
use of Generics in combination with `DefaultSignature` to provide
automatically derived default instances, most notably `hashable`[6],
`binary`[7], or `aeson`[8] just to name a few. In addition to providing
a precedence for the use of Generics, I consider those packages evidence
for Generics to have proven itself to the point of replacing
TemplateHaskell in these use-cases.


Compatibility & Breakage Considerations
=======================================

 * This change requires a major version bump to deepseq-1.4.0

 * `deepseq` needs to drop GHC 7.0.* support as GHC 7.2 is the first
   version to support Generics & `DefaultSignature`.

 * Code relying on the current `rnf` default-implementation will most
   likely break (unless a `Generics` instance happens to be in-place)

   However, it's easy to provide forward/backward-compatibility w/o any
   CPP, by simply explicitly defining

     instance NFData XYZ where rnf = seq x ()



Discussion Period: 2 weeks



 [1]: http://hackage.haskell.org/package/deepseq
 [2]: http://hackage.haskell.org/package/deepseq-generics
 [3]: https://github.com/haskell/deepseq/pull/1
 [4]: http://thread.gmane.org/gmane.comp.lang.haskell.libraries/17940
 [5]: https://ghc.haskell.org/trac/ghc/ticket/5462
 [6]: http://hackage.haskell.org/package/hashable
 [7]: http://hackage.haskell.org/package/binary
 [8]: http://hackage.haskell.org/package/aeson


More information about the Libraries mailing list