Closure conversion, monomorphisation and type erasure implemented on a Quantitative Dependently Typed Lambda Calculus (typechecked here).
Input terms are all assumed to be type-correct and already elaborated.
To run: ghc --make Main && ./Main.
Upon success, you should some output similar to this execution trace:
>> Performing monomorphisation of term @e@ where
e ≡ let id :^ω ∀(A : U). (x : A) ⊸ A = λ(A :^0 U) : ((x : A) ⊸ A). λ(x :^1 A) : A. x in (id ℕ 0 :^1 ℕ, id 𝟏 () : ())
>>> Success!
let "id𝟏" :^ω (x : 𝟏) ⊸ 𝟏 = λ(x :^1 𝟏) : 𝟏. x in let "idℕ" :^ω (x : ℕ) ⊸ ℕ = λ(x :^1 ℕ) : ℕ. x in ("idℕ" 0 :^1 ℕ, "id𝟏" () : ())
>> Performing closure conversion on term @e'@ where
e' ≡ let "id𝟏" :^ω (x : 𝟏) ⊸ 𝟏 = λ(x :^1 𝟏) : 𝟏. x in let "idℕ" :^ω (x : ℕ) ⊸ ℕ = λ(x :^1 ℕ) : ℕ. x in ("idℕ" 0 :^1 ℕ, "id𝟏" () : ())
>>> Success!
let clos0 :^ω (x : 𝟏) ⊸ 𝟏 = λ(x :^1 𝟏) : 𝟏. x in let clos1 :^ω (x : ℕ) ⊸ ℕ = λ(x :^1 ℕ) : ℕ. x in let "id𝟏" :^ω (x : 𝟏) ⊸ 𝟏 = clos0 in let "idℕ" :^ω (x : ℕ) ⊸ ℕ = clos1 in ("idℕ" 0 :^1 ℕ, "id𝟏" () : 𝟏)
The line under the first >>> Success! is the monomorphised term synthetised from the input term e.
The line under the second >>> Success! is the term resulting from closure conversion of the monomorphised term.
Here's the list of available input terms:
- Type annotations (in the erased theory):
Π(x :ⁱ A). Bis the dependent function type taking a value of typeAas input and returning a value of typeB(after consumingx).(x :ⁱ A) ⊗ Bis the multiplicative dependent pair type.𝟏is the multiplicative unit type.Uis the type of types and universes (hence we haveU :⁰ U)
- Expressions (with runtime presence):
λ(x :ᵖ A) :ⁱ B. eis a lambda expression taking a parameter of typeAwith usagepand returns the expressionewith typeB(usage is restricted toi).xis a simple bound variable (type can be determined by examining binding points, e.g. parameters of lambda abstractions).(a :ᵖ A, b : B)is the multiplicative dependent pair containing the valueaof typeAwith usagepand the valuebof typeB.()is the multiplicative unit value.f xis the basic function application that all of us know.let () as z = a in bis the common eliminator for the multiplicative unit. This evaluatesafirst and returns the result of evaluatingb.let x :ⁱ A = e in rlocally binds the identifierxto the value ofe(which is of typeA) and returns the value ofr(which may make use ofx).ndenotes a common natural number (0,2530, etc.).
Output terms are mostly the same as input terms, with a few variations:
- Type annotations:
@{ x :ⁱ A, y :ᵖ B, ... }is a record type with labels.
- Expressions:
@{ x :ⁱ A = e₁, y :ᵖ B = e₂, ... }is a literal record value where each label has a specific value.let (x :ⁱ A, y :ⁱ B) as z = a in bis the destructor for the multiplicative dependent pair.rec x :ᵖ A = e in ris aletbinding which allowseto usex(xis allowed to reference itself in its value).r.xis the record accessor, meaning that it returns the value at labelxin the recordr(assuming that it is present).
Records are necessary for closure conversion, to maintain the state of the closure (what each free variable is bound to when we are defining a lambda expression).