Open OsePedro opened 1 month ago
The unary/binary polymorphism is not the same kind as you are thinking. NFData1
works on objects of kind k -> Type
, not on terms of the kind Type
. In your example, even though for any NFData a
you have NFData (Foo a)
, you do not have NFData1 Foo
. This is captured by the quantified constraints in the class declarations for NFData1
and NFData2
, which instances then can assume without knowing beforehand on which type the application will be made.
I believe QuantifiedConstraints
is only available on newer versions of GHC. Please update if you can, using ghcup
or manually.
Yes, I'm aware that NFData1
expresses a constraint on types of kind k -> Type
. But it's not my example -- it's the example in this part of the module documentation. I just removed NFData1
from the original example to demonstrate that you can derive an NFData
instance without it.
From my point of view, the most important questions about NFData1
that the documentation doesn't make clear are:
liftRnf
or rnf1
are people who are manually deriving NFData
instances?NFData1
useful for?NFData1
is to NFData
what Eq1
is to Eq
: a lawful typeclass. Where Eq1
"enforces" liftEq (==) == (==)
, we should always have liftRnf rnf == rnf
for a NFData1
type constructor. In fact, the default implementation proves that law for any Generic1
data structure from its canonical representation Rep1
.
The only part of the interface that need concern the day-to-day developer are the "Helper functions". These are normally what you want for making sure your own data is in RNF at certain evaluation steps. For example, f $!! x
makes sure x
is in RNF before applying f
, but force (f $!! x)
makes sure that the result is also in RNF before continuing to be used by the program.
Right, and isn't the "Generic NFData deriving" section also aimed at the day-to-day developer, whose goal is to apply the "Helper functions" to their own data types? If so, wouldn't it be less confusing to remove the derived Generic1
and NFData1
instances from the example Foo a
type? After all, none of the "Helper functions" require NFData1
.
The "Generic NFData deriving" documentation section shows how to derive instances of both
NFData
andNFData1
forFoo
, but it doesn't say anything about when generic instances needNFData1
(theNFData1
documentation doesn't help either). As this is in theNFData
class documentation, for people like me who first encounteredNFData
through a package that depends on it (Criterion in my case), this gives the impression that there must be a mysterious technicality that requires you to deriveNFData1
for types with a single type parameter whenever you need anNFData
instance. This is what I thought until I experimented and found thatdeepseq
works fine on yourFoo
example without theNFData1
instance. E.g. in GHCi 8.10.7:I see from the source code that
rnf = rnf1
for functors like list,Maybe
andEither
, so I now wonder ifNFData1
andNFData2
are just helper classes that are only useful when you're manually derivingNFData
instances. Are there cases whereNFData
cannot be generically derived for unary/binary polymorphic types withoutNFData1/2
? If not, can the derivation ofNFData1
be removed from theFoo
examples, and can a note be added stating that generic instances do not requireNFData1/2
? Or ifNFData1/2
does need to be generically derived sometimes, can someone add an explanation to the documentation?