DatatypeContexts: has something changed since it was marked deprecated?

That email thread is interesting. In particular this part by Wadler:

The class constraints on data declarations were a simple way for the user to ask the compiler to enforce this invariant. They have compile-time effect only, no effect whatsoever on run-time (in particular, no dictionaries should be passed).

And in a later mail:

Because my interest is as much in documentation as in constraints on
how the program executes
[…]
Your `redundant pollution’ is exactly the effect I want to achieve!

So it seems to me Wadler wanted basically the behavior that GHC currently has: datatype contexts should introduce no dictionaries on their own, but only require that whenever such data types are used that there is also an explicit “redundant” constraint.

To be honest, I’m starting to come around to that viewpoint. Essentially data type contexts would be a form of machine checked documentation that prevents “silly” types. For example it prevents you from accidentally writing an Functor instance like this:

{-# LANGUAGE DatatypeContexts #-}

data Eq a => Set a = Set [a]

instance Functor Set where
  fmap f (Set xs) = Set (fmap f xs)

Without the constraint it would be totally legal, but obviously you would never want to write this because it does not uphold the otherwise implicit invariant of the set type (e.g. fmap (const 0) (Set [1,2,3]) would produce a malformed set: Set [0,0,0]).

Of course in an ideal world you’d write out the full invariant (with liquid or dependent types), but I think datatype contexts are a useful middle-ground.

Another advantage is that it would be possible to infer nominal role for datatypes with contexts, so you wouldn’t have to use RoleAnnotations in many cases.