Skip to content

feat: LambdaCalculus.LocallyNameless.Coc#392

Open
matthunz wants to merge 17 commits intoleanprover:mainfrom
matthunz:coc
Open

feat: LambdaCalculus.LocallyNameless.Coc#392
matthunz wants to merge 17 commits intoleanprover:mainfrom
matthunz:coc

Conversation

@matthunz
Copy link

@matthunz matthunz commented Mar 3, 2026

Summary

Adds basic syntax and typing judgments for the Calculus of Constructions following Coquand's algorithm. I tried to keep everything as close to other lambda calculi in this project as well as general code style. Some of the lemmas from that paper are noticeably missing but I'm hoping this can be an OK first cut 😃

LambdaCalculus.LocallyNameless.Coc

  • Term syntax
inductive Term (Var : Type u) : Type u
  /-- Bound term variables using a de-Bruijn index. -/
  | bvar : ℕ → Term Var
  /-- Free term variables. -/
  | fvar : Var → Term Var
  /-- Function application. -/
  | app : Term Var → Term Var → Term Var
  /-- Lambda abstraction. -/
  | abs : Term Var → Term Var → Term Var
  /-- Pi type. -/
  | pi : Term Var → Term Var → Term Var
  /-- Type universe. -/
  | type : ℕ → Term Var
  • Typing judgements
inductive Typing : Env Var → Term Var → Term Var → Prop
  /-- Variable lookup in Γ -/
  | var : Γ.Wf → ⟨x, A⟩ ∈ Γ → Typing Γ (.fvar x) A
  /-- Function application -/
  | app : Typing Γ M (.pi A B) → Typing Γ N A → Typing Γ (.app M N) (B ^ᵗ N)
  /-- Lambda abstraction -/
  | abs (ρ : Finset Var) :
      Typing Γ A K →
      (∀ x ∉ ρ, Typing ({⟨x, A⟩} ∪ Γ) (N ^ᵗ .fvar x) (B ^ᵗ .fvar x)) →
      Typing Γ (.abs A N) (.pi A B)
  /-- Pi type -/
  | pi (ρ : Finset Var) :
      Typing Γ A (.type k) →
      (∀ x ∉ ρ, Typing ({⟨x, A⟩} ∪ Γ) (B ^ᵗ .fvar x) (.type i)) →
      (i = k ∨ i = 0) →
      Typing Γ (.pi A B) (.type i)
  /-- Type universe -/
  | type : Typing Γ (.type s) (.type (s + 1))
  /-- β-conversion -/
  | conv : Typing Γ M A → A =β B → Typing Γ B (.type i) → Typing Γ M B

Cslib.Foundations.Syntax.HasBetaEquiv

  • HasBetaEquiv (similar to HasAlphaEquiv) for the notation A =β B
class HasBetaEquiv (t : Type u) where
  /-- β-equivalence relation for type t. -/
  BetaEquiv : t → t → Prop

@chenson2018
Copy link
Collaborator

Hi, thanks for your interest in contributing!

Can I ask why the jump to CoC for this named representation of binding? It is a little uncommon to do this style at all, so there's not much else to build off yet. I was hoping that STLC and System F would get filled out first to work out any kinks before more complex type systems.

We are planning to add well scoped indices as an alternative in the next few months or so, but if your interest is formalizing CoC right now would you be interested in doing so in the locally nameless style? This would be much easier to review in comparison to what we already have and prior work in Rocq.

@matthunz matthunz changed the title feat: LambdaCalculus.Named.Coc feat: LambdaCalculus.LocallyNameless.Coc Mar 4, 2026
@matthunz
Copy link
Author

matthunz commented Mar 4, 2026

Hey 👋 thanks so much for the feedback!

I wanted to go the named representation route at first because it felt more natural to me coming from functional programming but I'm definitely unsure of how to prove things like well-formedness with that approach.

I'd be thrilled if we can fit this in here somehow given the current plans, here's what I did for now:

  • Changed to locally-nameless AST b3c0b23
  • Added the requisite locally-closed and well-formed proofs 8a20752 13ec089

I think the biggest remaining features would now be reduction and the related proofs to tie everything together, which might be a somewhat significant undertaking 😅 happy to defer to your judgement on what the scope of this PR should be

Copy link
Collaborator

@chenson2018 chenson2018 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of this looks good, in particular thank you for mostly following conventions from the System F formalization. There are a few important places however where I'm not sure about the definitions. Because these are tricky to get right I suggest closely following prior work like the formalization from Charguéraud I link below.

/-- Pi type. -/
| pi : Term Var → Term Var → Term Var
/-- Type universe. -/
| type : Term Var
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want this to be ℕ → Term Var as well.

Comment on lines +55 to +57
abbrev Env (Var : Type u) := Finset (Var × Term Var)

def Env.dom [DecidableEq Var] : Env Var → Finset Var := Finset.image Prod.fst
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect we reuse LocallyNameless.Context instead of this abbrev.


/-- Variable opening of the ith bound variable. -/
@[scoped grind =]
def openingRec (i : ℕ) (s : Term Var) : Term Var → Term Var
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should name this consistently as openRec and open' with the other typesystems.

Comment on lines +90 to +91
| abs (L : Finset Var) : σ.LC → (∀ x ∉ L, LC (t₁ ^ᵗ fvar x)) → LC (abs σ t₁)
| pi (L : Finset Var) : σ.LC → (∀ x ∉ L, LC (t₁ ^ᵗ fvar x)) → LC (pi σ t₁)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For CoC, the variable σ is a term, as opposed to System F where it was a type. I'd update the variable names accordingly to use t₁ and t₂.


/-- A locally closed term is unchanged by opening. -/
lemma openingRec_lc [HasFresh Var] {σ τ : Term Var} (lc : σ.LC) : σ = σ⟦X ↝ τ⟧ := by
classical
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for decidable equality? I'd rather make this explicit as a typeclass parameter.

open Term

/-- β-equivalence. -/
inductive BetaEquiv : Term Var → Term Var → Prop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definition looks very different from what I expected. See for instance the definition in the Rocq formalization I closely followed for other type systems.

BetaEquiv := BetaEquiv

/-- Typing judgement -/
inductive Typing [DecidableEq Var] : Env Var → Term Var → Term Var → Prop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting to see a mutual block also defining well-formed contexts.

/-- An environment is well-formed if it binds each variable exactly once to a well-formed type. -/
inductive Env.Wf : Env Var → Prop
| nil : Wf {}
| cons : Wf Γ → Term.Wf Γ τ → x ∉ Γ.dom → Wf ({⟨x, τ⟩} ∪ Γ)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is right. Because of the dependent types aspect, we need a typing derivation before adding to the context, right?

open Term

/-- β-reduction. -/
inductive BetaEquiv : Term Var → Term Var → Prop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have mentioned previously. This should use the reduction_sys attribute to get notation, because this is still reduction and not the equivalence (reflexive symmetric transitive closure), right? I would remove the HasBetaEquiv class and use this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants