Clean up printing of foralls
Simon and I agreed on several infelicities around the printing of types with foralls. Before cleaning up the code, though, we wanted a specification of what we should do. Here is what we have.
When printing types for users (in GHCi and certain error messages), it is convenient to sometimes suppress forall
s. Given a type ty
to print, forall
s will be suppressed when all of the following are true:
-
-fprint-explicit-foralls
is not in effect. - The kinds of all bound type variables are Haskell98-style. That is, the kinds consist only of
Type
and->
. - No bound type variable is
Required
. - The
forall
s are all at the top. Exception:forall
s are allowed to be mixed with class constraints, but noforall
s can appear under a proper->
. - No two quantified type variables are spelled the same.
This decision is made once, while looking at the overall type. Once made, it is not challenged: either all forall
s in a type are printed, or none are. Notice that if any foralls are suppressed, then they are all at the "top" of the type (albeit possibly under class constraints).
Reasons behind the conditions:
- No comment. The only effect of
-fprint-explicit-foralls
should be to turn off the suppression of foralls, and print all foralls unconditionally. - Currently, we print
forall k (a :: k). ...
when there is a bound type variable with a variable kind. But, really, the fact that it's a variable isn't the only way it could be interesting. E.g.forall (a :: Nat). blah
-
Required
type variables must be supplied; omitting these would be very confusing. E.b. inT :: forall k -> k -> Type
it would be jolly confusing to omit theforall k ->
part, leavingT :: k -> Type
. - If a type has nested
forall
s, it's best to print all the foralls, including any at the top. Example
f :: forall a. (forall b. b -> b) -> a -> a
Currently we suppress the outer forall, but with the nested forall we are well out of Haskell98-land and it seems better to display them all.
Reason for exception: the type of return
is properly
return :: forall (m :: Type -> Type). Monad m =>
forall (a :: Type). a -> m a
Note that a forall
is under a class constraint. But we want to suppress the forall
s there, too, to display
return :: Monad m => a -> m a
- Imagine this abuse of our language:
class C a b where
meth :: forall a. a -> b
The type of meth
is forall a b. C a b => forall a. a -> b
. This is, actually, a well-formed type, where one type variable shadows another. But if we don't print the forall
s, any reader will come to the wrong conclusion about the meaning of the type.
This is a slight tweak to the current rules, meaning more forall
s are printed than previously. Pointedly, this will not happen in Haskell98-style code, so they will not appear for beginners. The current state of affairs is messy, on the other hand, where some forall
s may be suppressed in a type while others are printed, causing more trouble.
While in town, any fix should also address