A syntax for visible dependent quantification¶
GHC 8.0 has support for visible dependent quantification in kinds. For example, GHC 8 will accept
data T k (a :: k)
Note that any (fully-applied) use of T
has to mention both a kind and a type. For example,
T Nat 3
and T Type Int
are well-typed. If you ask GHCi for the kind of T
, it tells
you forall k -> k -> *
(with -fprint-explicit-foralls
). Note that there is no .
after
the forall
. Instead, this kind makes k
a visible, dependent type variable. It’s visible
because every (fully-applied) use of T
must pass in a value for k
visibly (explicitly).
It’s dependent because k
appears later in the kind of T
. The ability to have visible,
dependent quantifiees is a new feature in GHC 8, available only in kinds. (There’s no way to do
this in terms.)
The syntax forall k -> k -> *
that is printed in GHCi for these kinds is not currently parsed.
This proposal proposed to add this kind format to the language as a first-class kind.
This proposal is viable on its own, but it may be best considered in the context of other quantifiers needed to support dependent types. See #102.
Motivation¶
Currently, GHC allows the user to define a type with a visible, dependent quantifiers in its kind, but it offers no way to write the kind directly. This is just plain silly.
(Historical note: the reason this happened is that I got early feedback claiming that forall k -> ...
was poor syntax, and I was reticent to add it to the language with -XTypeInType
. However, now that
this proposals process is active, I’m happy to propose the new syntax where it can get debated in the
open and perhaps refined.)
A useful example of how this construct might be used is an alternative syntax for TypeRep
. We can
imagine
TypeRepV :: forall k -> k -> Type -- "V" for visible
as a companion to
TypeRep :: forall k. k -> Type -- we have this today
Now, if someone wants to operate on
a type representation for some type of kind Type -> Type
, they could say
foo :: TypeRepV (Type -> Type) a -> ...
and GHC would easily infer that a
should have kind Type -> Type
.
Proposed Change Specification¶
Add a new bit of syntax for types (= kinds) that looks like this:
'forall' tv_bndrs '->' ctype
where ctype
is the point within GHC’s grammar (as implemented in its
parser)
where the current 'forall' tv_bndrs '.' ctype
rule lives. The meaning will
be identical to that of the existing forall
construct, except that the
tv_bndrs
will be visible.
(NB: The 'forall'
construct in the parser also accepts ∀
.)
This new construct will be rejected in any context that is unambiguously a type for a term. (For example, it will be rejected in type signatures of terms, but allowed in type synonyms, which can be used in kinds.) No term-level definition can have a type that has a visible dependent quantifier.
To wit, this new construct would be forbidden in the following places (with examples of rejected constructs):
The type ascription of a
forall
-bound term variable in aRULE
:{-# RULES "blah" forall a -> id a = a #-}
The type of a foreign import/export:
foreign export ccall freeStablePtr :: forall a -> StablePtr a -> IO ()
A type signature for a term-level variable:
id :: forall a -> a -> a
The type in a
SPECIALISE
orSPECIALISE_INLINE
orSPECIALISE instance
pragma:{-# SPECIALISE foldM :: forall a b -> (a -> b -> IO a) -> a -> [b] -> IO a #-}
An expression type ascription:
zipWith ((<>) :: forall a -> Maybe a -> Maybe a -> Maybe a) xs ys
A pattern synonym type signature:
pattern Nil :: forall a -> [a]
A type signature in a pattern:
isJust (x :: forall a -> Maybe a) = ...
A GADT data constructor type:
data T where MkT :: forall a -> a -> T
Naturally, the new syntax is forbidden anywhere that forall
is currently
forbidden (for example, in an argument position of a type family).
Effect and Interactions¶
Shouldn’t be any untoward interactions. Template Haskell will have to be updated, and we’ll have to make sure no terms can get these strange new types.
Note that the new construct can be used in higher-rank scenarios:
data S :: (forall k -> k -> Type) -> Type
will accept the T
in the introduction as an argument, but it won’t accept Data.Proxy
's
Proxy
, as Proxy
takes its argument invisibly. Perhaps one day we can devise a way
to coerce visibilities to allow S
to take Proxy
as an argument, but not today.
Costs and Drawbacks¶
It’s one more construct that has to be maintained, which is a non-negligible cost. But, I argue that the language simply has a strange surface area without this feature, where a type exists that cannot be written down.
A drawback of the design as proposed is that the signifier of the visible/invisible distinction can
be far away from individual variables. For example, consider forall a b c d.
and forall a b c d ->
.
You have to scan for the .
or the ->
before you know what kind of quantification is at hand.
Alternatives¶
I don’t have any good ones. Do you?
Unresolved questions¶
None right now, other than bikeshedding this syntax.
Implementation Plan¶
I or a close collaborator volunteer to implement.