Skip to content

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:

  1. Step 1. Identify all discrepancies between HsExpr and HsType, and fix them one by one. List of already identified problems:
    • #18782 (closed). HsType has constructors HsRecTy and HsBangTy. Arguably they shouldn't be there, so instead of adding them HsExpr, we can try removing them from HsType. Continue discussion at #18782 (closed)
    • #18758. NoGhcTc is only used in HsExpr, e.g. HsAppType has it whereas HsAppKindTy doesn't. We could either put NoGhcTc in more places or try to get rid of it. Continue discussion at #18758
    • HsTyVar has a PromotionFlag, HsVar doesn't. Easily solved by adding PromotionFlag to HsExpr, even though it can't be parsed by the expression grammar.
    • HsOpTy represents the operator with just its name (i.e. LIdP pass), whereas OpApp admits a subexpression (i.e. HsExpr pass). We can make the transition smoother if we use HsType pass in HsOpTy.
    • HsStarTy doesn't exist in HsExpr
    • Feel free to add to this list if you spot more discrepancies, or I will do that as I discover them during implementation
  2. Step 2. Start using HsExpr instead of HsType 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 of panics to deal with HsExpr constructors that do not actually occur in types.
  3. 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.
Edited by Vladislav Zavialov
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information