[Haskell-cafe] Design for DatatypeContexts: what was the idea, anyway?

Anthony Clayden anthony_clayden at clear.net.nz
Tue Sep 15 12:57:18 UTC 2020

Quite prosaically: the design for DatatypeContexts seems to have
materialised with no visible discussion, neither agreement nor
disagreement; and persisted almost unchanged until they got deprecated
nearly 20 years later.

I can guess there was a verbal discussion amongst the Committee circa
1990/91; that somebody was deputed to write down the conclusion (memo March
1991 [1]) ; and the implementers (both Committee members) went to work in
Hugs and GHC. Much later (1999, while preparing the Haskell 1998 Report,
published 2002) a fresh set of eyes spotted an under-explicitness that had
been interpreted differently by the implementers; and there was a Committee
thread resolving it. That thread is the best place from which to understand
the intent, particularly Phil Wadler's comments [2].

Historical context: typeclasses/constraints/contexts were the new shiny
thing, still rapidly evolving (Wadler early 1989, Wadler & Blott late 1989
but with several open questions). They were initially tied to overloading
methods; by 1990 the Haskell report v1.0 included constraints for Numeric
Literals, but with several caveats. The mailing lists had much
back-and-forth about ambiguities and defaulting and (of course) the Dreaded
Monomorphism Restriction for constrained terms. I see comments about the
"dictionary transfer problem" and dictionaries "shared lazily between all
applications to each elt [in a data structure]".

The syntax of prefixing the context to the type constructor name (syntax in
the 1990 Haskell Report, along with the admittedly vague descriptions
there), suggests to me they were thought of as for numeric literals: `5 ::
Num a => a` so `(ConsSet 5 NilSet) :: Num a => Set a`, where Set's `Eq a`
is implied, as a superclass of Num. There's no visible method invocation.
If the intent had been that the context attached only inside the data
constructors, I'd expect syntax like one of these:

    data Set a = NilSet | (Eq a) => ConsSet a (Set a)
    data Set a = NilSet | ConsSet (Eq a) => a (Set a)

(Syntax closer to that second did arrive later with GADTs.)

The 1999 discussion concluded that the context should be exposed outside of
a pattern match, even though a match only 'consumes' an already constructed
value. Exposing makes sense if all data constructors enforce the context,
but from the 1991 design, `NilSet`'s type doesn't mention the context
(because there's no argument to the constructor to ground the type?). I'm
confused by that: if Haskell 1990 supported `5 :: Num a => a`, what's wrong
with `NilSet :: Eq a => Set a`?

I sense quite a bit of exasperation in that 1991 memo: "Nobody has been
able to give a satisfactory account", "Nobody has been able to explain",
several phrases in block caps. The so-called "simplifications" are almost
what Parliamentarians would call a 'wrecking amendment' (undermining or
nullifying the intent of a proposal) or even applying Cunningham's Law (the
quickest way to get the right answer is not to ask a question, but to post
a wrong answer) -- except nobody (visibly) pushed back.

And there's hints of dissent unresolved up to the 1999 exchange: "a broken
feature of H98", "What you propose, I think, offers the worst of both worlds
",   "a compile-time language feature can be hard to understand if it is not
directly tied to run-time behaviour.",  "Live and learn.". (Who should
learn? Is that a self-admonition or aimed at others?)

It's well known that SPJ dubbed the feature "stupid theta". The thing is:
the scribe in 1991 was SPJ; even if he was only writing from dictation, why
specify and then implement a "broken feature"? Or did it only earn the
"stupid" as a result of the 1999 decision? Then it's notable in that
decision that Hugs' behaviour was preferred; GHC was changed to align; but
then GADTs design reverted to the GHC pre-1999 behaviour (which is
reasonable considering different constructors have different constraints
and return types).

Pattern Synonyms support double-contexts in the signature, to be explicit
about what's exposed/required outside a pattern match.

I see much recent traffic (say on StackOverflow) that GADTs are
DatatypeContexts 'done right'. I can't agree with that: GADTs are
addressing a different set of use cases vs. the motivation Wadler explains
in 1999 -- to enforce invariants -- which makes sense to me.


(The question is posed in the preceding message in the thread; follow the
thread through to see the number of luminaries disagreeing with GHC's then
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20200916/efdf86ed/attachment.html>

More information about the Haskell-Cafe mailing list