wiki:NoDatatypeContexts

Version 1 (modified by igloo, 4 years ago) (diff)

--

Proposal: NoDatatypeContexts

Ticket (TODO)
Dependencies
Related

Compiler support

GHC full (-XNoDatatypeContexts)
nhc98 none
Hugs none
UHC none
JHC none
LHC none

Summary

H98 and H2010 allow a context to be given for datatypes, e.g.

data Eq a => Foo a = Constr a

This proposal is to remove support for that context.

Description

Datatype contexts (called "stupid theta" in GHC sources) appear, at first glance, to be very useful, but in actual fact they are not as useful as they seem.

One might expect that, with the above Foo type, one could write

isEq :: Foo a -> Foo a -> Bool
isEq (Constr x) (Constr y) = x == y

and the compiler would know that there is an Eq a instance from the datatype context. However, this is not the case. We instead must write

isEq :: Eq a => Foo a -> Foo a -> Bool
isEq (Constr x) (Constr y) = x == y

In fact, we cannot even write

getVal :: Foo a -> a
getVal (Constr x) = x

We must write

getVal :: Eq a => Foo a -> a
getVal (Constr x) = x

instead.

When we would expect the compiler to have the instance already available, in actual fact we have to provide it ourselves. The problem is that the constraint is not actually on the datatype, but rather, on the type of the constructor. Worse, the dictionary is not stored in the constructor, but simply discarded.

It is possible to write programs whose behaviour changes when datatype contexts are removed, e.g. with:

data Floating a => Foo a = Constr a

getIncVal (Constr x) = x + 1

foo = show (getIncVal (Constr 1))

foo is "2.0", while with

data Foo a = Constr a

getIncVal (Constr x) = x + 1

foo = show (getIncVal (Constr 1))

foo is "2". Likewise it is possible that a program will fail to compile when datatype contexts are removed. However, in practice removing datatype contexts is unlikely to affect the behaviour of any real programs.

What it will do is to make some previously illegal programs legal. However, we do not believe that the power that this provides is worth the burden the language construct places on implementors, documentors and educators. Other extensions being worked on, such as GADTs, have a much higher power to weight ratio.

Current usage

There are 4 modules in base 4.3 that use datatype contexts:

  • Control.Arrow
  • Data.Complex
  • GHC.Arr
  • GHC.Real

In most cases, the type is specified in the H98 and H2010 report.

Doing a Hackage regression test with GHC 6.13.20100710, of the 2219 packages on Hackage, cabal-install is able to make an installation plan for 920, and GHC is actually able to compile 590. Of those 590, these 15 then fail if datatype contexts are turned off:

  • MonadRandom
  • PriorityChansConverger
  • Yampa
  • chp
  • cpsa
  • fgl
  • fixed-list
  • gpcsets
  • mtlx
  • parseargs
  • priority-sync
  • ranges
  • redis
  • reord
  • stream-fusion

Making these packages buildable is assumed to be as simple as removing the datatype contexts, but this has not been verified.

Report Delta

Changes relative to H2010 report.

In Section 4:

Replace:

topdecl ->
 ...
 | data [context =>] simpletype [= constrs] [deriving]
 | newtype [context =>] simpletype = newconstr [deriving]
 ...

with:

topdecl ->
 ...
 | data simpletype [= constrs] [deriving]
 | newtype simpletype = newconstr [deriving]
 ...

In Section 4.2.1:

Replace:

topdecl -> data [context =>] simpletype [= constrs] [deriving]

with:

topdecl -> data simpletype [= constrs] [deriving]

Replace:

An algebraic datatype declaration has the form: data cx => T u1 … uk = K1 t11 … t1k1 | ⋅⋅⋅ | Kn tn1 … tnkn  where cx is a context.

with:

An algebraic datatype declaration has the form: data T u1 … uk = K1 t11 … t1k1 | ⋅⋅⋅ | Kn tn1 … tnkn.

Replace:

The types of the data constructors are given by: Ki :: ∀ u1 … uk. cxi ⇒ ti1 → ⋅⋅⋅ → tiki → (T u1 … uk)
where cxi is the largest subset of cx that constrains only those type variables free in the types ti1, …, tiki.
The type variables u1 through uk must be distinct and may appear in cx and the tij; it is a static error for
any other type variable to appear in cx or on the right-hand-side.

with:

The types of the data constructors are given by: Ki :: ∀ u1 … uk. ti1 → ⋅⋅⋅ → tiki → (T u1 … uk).
The type variables u1 through uk must be distinct and may appear in the tij; it is a static error for any other
type variable to appear on the right-hand-side.

Replace:

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

with:

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

Replace:

NilSet :: ∀ a. Set a
ConsSet :: ∀ a. Eq a ⇒ a → Set a → Set a

In the example given, the overloaded type for ConsSet ensures that ConsSet can only be applied to values whose type
is an instance of the class Eq. Pattern matching against ConsSet also gives rise to an Eq a constraint. For example:
  f (ConsSet a s) = a
the function f has inferred type Eq a => Set a -> a. The context in the data declaration has no other effect whatsoever.

with:

NilSet :: ∀ a. Set a
ConsSet :: ∀ a. a → Set a → Set a

Replace:

Translation: A declaration of the form data cx => T u1 … uk = … | K s1 … sn | … where each si is either of the form !ti or ti,
replaces every occurrence of K in an expression by (\ x1 … xn -> ( ((K op1 x1) op2 x2) … ) opn xn)  where opi is the non-strict
apply function $ if si is of the form ti, and opi is the strict apply function $! (see Section 6.2) if si is of the form ! ti.
Pattern matching on K is not affected by strictness flags.

with:

Translation: A declaration of the form data T u1 … uk = … | K s1 … sn | … where each si is either of the form !ti or ti,
replaces every occurrence of K in an expression by (\ x1 … xn -> ( ((K op1 x1) op2 x2) … ) opn xn)  where opi is the non-strict
apply function $ if si is of the form ti, and opi is the strict apply function $! (see Section 6.2) if si is of the form ! ti.
Pattern matching on K is not affected by strictness flags.

In Section 4.2.3:

Replace:

topdecl → newtype [context =>] simpletype = newconstr [deriving]

with:

topdecl → newtype simpletype = newconstr [deriving]

Replace:

A declaration of the form newtype cx => T u1 … uk = N t introduces [...]

with:

A declaration of the form newtype T u1 … uk = N t introduces [...]

In Section 10.5:

Replace:

topdecl ->
 ...
 | data [context =>] simpletype [= constrs] [deriving]
 | newtype [context =>] simpletype = newconstr [deriving]
 ...

with:

topdecl ->
 ...
 | data simpletype [= constrs] [deriving]
 | newtype simpletype = newconstr [deriving]
 ...