Unify HsExpr and HsType
This ticket tracks a refactoring project within GHC, whose goal is to arrive at the following declaration:
type HsType = HsExpr
The rationale for this is to increase code reuse between the term- and type-level code in the compiler front-end (AST, parser, renamer, type checker).
A more ambitious idea (also a bit more contentious) is to throw Pat
into the mix, and it is discussed here: https://github.com/ghc-proposals/ghc-proposals/discussions/663. In this ticket we limit the scope to unifying HsExpr
and HsType
.
The greatest piece of evidence that this unification is a good idea comes from the fact that we need to embed the entirety of type syntax in expressions (8b2f70a2) in order to implement GHC Proposal #281.
So we need constructors in HsExpr
to represent forall tvs. t
, ctx => t
, a -> b
, etc. Previously those were unique to HsType
, so a case could be made for keeping HsType
separate; now HsType
is morally a subset of HsExpr
(technically there are a few discrepancies to be resolved, e.g. NoGhcTc
, HsBangTy
, HsRecTy
).
Here is how I propose to conduct this refactoring:
-
Step 1. Identify all discrepancies between
HsExpr
andHsType
, and fix them one by one. List of already identified problems:-
#18782. HsType
has constructorsHsRecTy
andHsBangTy
. Arguably they shouldn't be there, so instead of adding themHsExpr
, we can try removing them fromHsType
. Continue discussion at #18782 -
#18758. NoGhcTc
is only used inHsExpr
, e.g.HsAppType
has it whereasHsAppKindTy
doesn't. We could either putNoGhcTc
in more places or try to get rid of it. Continue discussion at #18758 -
HsTyVar
has aPromotionFlag
,HsVar
doesn't. Easily solved by addingPromotionFlag
toHsExpr
, even though it can't be parsed by the expression grammar. -
HsOpTy
represents the operator with just its name (i.e.LIdP pass
), whereasOpApp
admits a subexpression (i.e.HsExpr pass
). We can make the transition smoother if we useHsType pass
inHsOpTy
. -
HsStarTy
doesn't exist inHsExpr
- Feel free to add to this list if you spot more discrepancies, or I will do that as I discover them during implementation
-
-
Step 2. Start using
HsExpr
instead ofHsType
in the existing code that works on types. If the previous step was done correctly, this should be a rather mechanical rewrite. The only issue I can foresee is that we'll need a bunch ofpanic
s to deal withHsExpr
constructors that do not actually occur in types. -
Step 3. Identify opportunities for code reuse.
- We can have just one parser for types and terms. (It would be up to the renamer or typechecker to then complain if it found, say, a list comprehension inside a type.)
- We can have just one renamer that would work for both types and terms. We only need to parameterize it by the default namespace for lookups.
- The T2T transformation in the type checker would no longer be necessary.