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
HsExprandHsType, and fix them one by one. List of already identified problems:-
#18782 (closed). HsTypehas constructorsHsRecTyandHsBangTy. Arguably they shouldn't be there, so instead of adding themHsExpr, we can try removing them fromHsType. Continue discussion at #18782 (closed) -
#18758. NoGhcTcis only used inHsExpr, e.g.HsAppTypehas it whereasHsAppKindTydoesn't. We could either putNoGhcTcin more places or try to get rid of it. Continue discussion at #18758 -
HsTyVarhas aPromotionFlag,HsVardoesn't. Easily solved by addingPromotionFlagtoHsExpr, even though it can't be parsed by the expression grammar. -
HsOpTyrepresents the operator with just its name (i.e.LIdP pass), whereasOpAppadmits a subexpression (i.e.HsExpr pass). We can make the transition smoother if we useHsType passinHsOpTy. -
HsStarTydoesn'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
HsExprinstead ofHsTypein 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 ofpanics to deal withHsExprconstructors 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.