Note [The stupid context]
Data types can have a context:
data (Eq a, Ord b) => T a b = T1 a b | T2 a
And that makes the constructors have a context too. A constructor's context
isn't necessarily the same as the data type's context, however. Per the
Haskell98 Report, the part of the datatype context that is used in a data
constructor is the largest subset of the datatype context that constrains
only the type variables free in the data constructor's field types. For
example, here are the types of T1 and T2:
T1 :: (Eq a, Ord b) => a -> b -> T a b
T2 :: (Eq a) => a -> T a b
Notice that T2's context is "thinned". Since its field is of type `a`, only
the part of the datatype context that mentions `a`—that is, `Eq a`—is
included in T2's context. On the other hand, T1's fields mention both `a`
and `b`, so T1's context includes all of the datatype context.
Furthermore, this context pops up when pattern matching
(though GHC hasn't implemented this, but it is in H98, and
I've fixed GHC so that it now does):
f (T2 x) = x
gets inferred type
f :: Eq a => T a b -> a
I say the context is "stupid" because the dictionaries passed
are immediately discarded -- they do nothing and have no benefit.
(See Note [Instantiating stupid theta].)
It's a flaw in the language.
GHC has made some efforts to correct this flaw. In GHC, datatype contexts
are not available by default. Instead, one must explicitly opt in to them by
using the DatatypeContexts extension. To discourage their use, GHC has
deprecated DatatypeContexts.
Some other notes about stupid contexts:
* Stupid contexts can interact badly with `deriving`. For instance, it's
unclear how to make this derived Functor instance typecheck:
data Eq a => T a = MkT a
deriving Functor
This is because the derived instance would need to look something like
`instance Functor T where ...`, but there is nowhere to mention the
requisite `Eq a` constraint. For this reason, GHC will throw an error if a
user attempts to derive an instance for Functor (or a Functor-like class)
where the last type variable is used in a datatype context. For Generic(1),
the requirements are even harsher, as stupid contexts are not allowed at all
in derived Generic(1) instances. (We could consider relaxing this requirement
somewhat, although no one has asked for this yet.)
Stupid contexts are permitted when deriving instances of non-Functor-like
classes, or when deriving instances of Functor-like classes where the last
type variable isn't mentioned in the stupid context. For example, the
following is permitted:
data Show a => T a = MkT deriving Eq
Note that because of the "thinning" behavior mentioned above, the generated
Eq instance should not mention `Show a`, as the type of MkT doesn't require
it. That is, the following should be generated (#20501):
instance Eq (T a) where
(MkT == MkT) = True
* It's not obvious how stupid contexts should interact with GADTs. For this
reason, GHC disallows combining datatype contexts with GADT syntax. As a
result, dcStupidTheta is always empty for data types defined using GADT
syntax. References 1
- Instantiating stupid theta GHC.Core.DataCon
Referenced by 14
- GHC.Core.DataCon call site ×2
- GHC.Core.TyCon call site ×2
- GHC.Core.ConLike call site
- Instantiating stupid theta GHC.Core.DataCon
- GHC.Rename.Module call site
- Requirements for deriving Generic and Rep GHC.Tc.Deriv.Generics
- GHC.Tc.Deriv.Infer call site
- GHC.Tc.Deriv.Utils call site
- GHC.Tc.Gen.Pat call site
- GHC.Tc.TyCl call site
- GHC.Tc.TyCl.Build call site
- GHC.Tc.TyCl.Utils call site