Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Cslib/Foundations/Syntax/HasBetaEquiv.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/-
Copyright (c) 2026 Matt Hunzinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Matt Hunzinger
-/

module

public import Cslib.Init

public section

namespace Cslib

/-- Typeclass for the Ξ²-equivalence notation `x =Ξ² y`. -/
class HasBetaEquiv (Ξ± : Type u) where
/-- Ξ²-equivalence relation for type Ξ±. -/
BetaEquiv : Ξ± β†’ Ξ± β†’ Prop

@[inherit_doc]
notation m:max " =Ξ² " n:max => HasBetaEquiv.BetaEquiv m n

end Cslib
62 changes: 62 additions & 0 deletions Cslib/Languages/LambdaCalculus/LocallyNameless/Coc/Basic.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/-
Copyright (c) 2026 Matt Hunzinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Matt Hunzinger
-/

module

public import Cslib.Foundations.Data.HasFresh
public import Cslib.Languages.LambdaCalculus.LocallyNameless.Context

@[expose] public section

/-! # Calculus of Constructions

The Calculus of Constructions

## References

* [T. Coquand, *An algorithm for type-checking dependent types*][Coquand1996]

* [A. Chargueraud, *The Locally Nameless Representation*][Chargueraud2012]

-/

namespace Cslib

universe u

namespace LambdaCalculus.LocallyNameless.Coc

/-- Syntax of locally nameless terms, with free variables over `Var`. -/
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
deriving DecidableEq

/-- Free variables. -/
@[scoped grind =]
def Term.fv [DecidableEq Var] : Term Var β†’ Finset Var
| .bvar _ => βˆ…
| .fvar z => {z}
| .app f a => f.fv βˆͺ a.fv
| .abs t b => t.fv βˆͺ b.fv
| .pi t b => t.fv βˆͺ b.fv
| .type _ => βˆ…

abbrev Env (Var : Type u) := Context Var (Term Var)

end LambdaCalculus.LocallyNameless.Coc

end Cslib
128 changes: 128 additions & 0 deletions Cslib/Languages/LambdaCalculus/LocallyNameless/Coc/Opening.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/-
Copyright (c) 2026 Matt Hunzinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Matt Hunzinger
-/

module

public import Cslib.Foundations.Data.HasFresh
public import Cslib.Foundations.Syntax.HasSubstitution
public import Cslib.Languages.LambdaCalculus.LocallyNameless.Coc.Basic

@[expose] public section

/-! # Calculus of Constructions

The Calculus of Constructions

## References

* [T. Coquand, *An algorithm for type-checking dependent types*][Coquand1996]

* [A. Chargueraud, *The Locally Nameless Representation*][Chargueraud2012]

-/

set_option linter.unusedDecidableInType false

namespace Cslib

universe u

namespace LambdaCalculus.LocallyNameless.Coc

namespace Term

/-- Variable opening of the ith bound variable. -/
@[scoped grind =]
def openRec (i : β„•) (s : Term Var) : Term Var β†’ Term Var
| .bvar y => if i = y then s else (bvar y)
| .fvar x => fvar x
| .app f a => .app (openRec i s f) (openRec i s a)
| .abs Οƒ t₁ => abs (openRec i s Οƒ) (openRec (i + 1) s t₁)
| .pi Οƒ t₁ => .pi (openRec i s Οƒ) (openRec (i + 1) s t₁)
| .type s => .type s

/-- Variable opening of the closest binding. -/
@[scoped grind =]
def open' (s : Term Var) (t : Term Var) : Term Var := openRec 0 t s

@[inherit_doc]
scoped infixr:80 " ^α΅— " => open'

@[inherit_doc]
scoped notation:68 Ξ³ "⟦" X " ↝ " Ξ΄ "⟧"=> openRec X Ξ΄ Ξ³

/--
Capture-avoiding substitution.
`m.subst x r` replaces the free occurrences of variable `x` in `m` with `r`.
-/
@[scoped grind =]
def subst [DecidableEq Var] (m : Term Var) (x : Var) (r : Term Var) : Term Var :=
match m with
| .bvar n => .bvar n
| .fvar z => if z = x then r else .fvar z
| .app f a => .app (f.subst x r) (a.subst x r)
| .abs t b => .abs (t.subst x r) (b.subst x r)
| .pi t b => .pi (t.subst x r) (b.subst x r)
| .type s => .type s

/-- `Term.subst` is a substitution for Ξ»-terms. Gives access to the notation `m[x := n]`. -/
instance instHasSubstitutionTerm [DecidableEq Var] :
HasSubstitution (Term Var) Var (Term Var) where
subst := Term.subst

@[scoped grind _=_]
lemma subst_def [DecidableEq Var] :
Term.subst (m : Term Var) (x : Var) (r : Term Var) = m[x := r] := rfl

/-- Substitution of a free variable not present in a term leaves it unchanged. -/
lemma subst_fresh [DecidableEq Var] {Ξ³ : Term Var}
(nmem : X βˆ‰ Ξ³.fv) (Ξ΄ : Term Var) : Ξ³ = Ξ³[X := Ξ΄] := by
induction Ξ³ <;> grind

/-- An opening appearing in both sides of an equality of terms can be removed. -/
lemma openRec_neq_eq (neq : x β‰  y) (eq : t⟦y ↝ sβ‚βŸ§ = t⟦y ↝ sβ‚βŸ§βŸ¦x ↝ sβ‚‚βŸ§) :
t = t⟦x ↝ sβ‚‚βŸ§ := by
induction t generalizing x y <;> grind

/-- Locally closed terms. -/
inductive LC : Term Var β†’ Prop
| var : LC (.fvar x)
| app : LC t₁ β†’ LC tβ‚‚ β†’ LC (app t₁ tβ‚‚)
| abs (L : Finset Var) : t₁.LC β†’ (βˆ€ x βˆ‰ L, LC (tβ‚‚ ^α΅— fvar x)) β†’ LC (abs t₁ tβ‚‚)
| pi (L : Finset Var) : t₁.LC β†’ (βˆ€ x βˆ‰ L, LC (tβ‚‚ ^α΅— fvar x)) β†’ LC (pi t₁ tβ‚‚)
| type : LC (.type _)

attribute [scoped grind .] LC.var LC.app LC.type

/-- Predicate for being a locally closed body of an abstraction. -/
@[scoped grind =]
def body (t : Term Var) := βˆƒ L : Finset Var, βˆ€ x βˆ‰ L, LC (t ^α΅— fvar x)

/-- A locally closed term is unchanged by opening. -/
lemma openRec_lc [DecidableEq Var] [HasFresh Var] {Οƒ Ο„ : Term Var} (lc : Οƒ.LC) : Οƒ = ΟƒβŸ¦X ↝ Ο„βŸ§ := by
induction lc generalizing X with
| abs | pi => grind [fresh_exists <| free_union Var, openRec_neq_eq]
| _ => grind

/-- Substitution of a locally closed type distributes with opening. -/
lemma openRec_subst [DecidableEq Var] [HasFresh Var] {Ξ΄ : Term Var}
(Y : β„•) (t₁ tβ‚‚ : Term Var) (lc : Ξ΄.LC) (X : Var) :
(tβ‚βŸ¦Y ↝ tβ‚‚βŸ§)[X := Ξ΄] = t₁[X := Ξ΄]⟦Y ↝ tβ‚‚[X := Ξ΄]⟧ := by
induction t₁ generalizing Y <;> grind [openRec_lc]

/-- A locally closed term remains locally closed after substitution. -/
lemma subst_lc [DecidableEq Var] [HasFresh Var] {t₁ tβ‚‚ : Term Var}
(t₁_lc : t₁.LC) (tβ‚‚_lc : tβ‚‚.LC) (X : Var) : t₁[X := tβ‚‚].LC := by
induction t₁_lc with
| abs => grind [LC.abs (free_union Var), openRec_subst]
| pi => grind [LC.pi (free_union Var), openRec_subst]
| _ => grind [openRec_subst]

end Term

end LambdaCalculus.LocallyNameless.Coc

end Cslib
117 changes: 117 additions & 0 deletions Cslib/Languages/LambdaCalculus/LocallyNameless/Coc/Typing.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/-
Copyright (c) 2026 Matt Hunzinger. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Matt Hunzinger
-/

module

public import Cslib.Foundations.Data.HasFresh
public import Cslib.Foundations.Syntax.HasAlphaEquiv
public import Cslib.Foundations.Syntax.HasBetaEquiv
public import Cslib.Foundations.Syntax.HasSubstitution
public import Cslib.Languages.LambdaCalculus.LocallyNameless.Coc.Opening

@[expose] public section

/-! # Calculus of Constructions
The Calculus of Constructions
## References
* [T. Coquand, *An algorithm for type-checking dependent types*][Coquand1996]
* [A. Chargueraud, *The Locally Nameless Representation*][Chargueraud2012]
-/

namespace Cslib

universe u

namespace LambdaCalculus.LocallyNameless.Coc

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.

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.

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.

/-- Ξ²-redex: `(Ξ» A. B) N ⟢ B ^α΅— N`. -/
| red : (abs A B).LC β†’ N.LC β†’ BetaEquiv (.app (.abs A B) N) (B ^α΅— N)
/-- Congruence in the function position of an application. -/
| app₁ : tβ‚‚.LC β†’ BetaEquiv t₁ t₁' β†’ BetaEquiv (.app t₁ tβ‚‚) (.app t₁' tβ‚‚)
/-- Congruence in the argument position of an application. -/
| appβ‚‚ : t₁.LC β†’ BetaEquiv tβ‚‚ tβ‚‚' β†’ BetaEquiv (.app t₁ tβ‚‚) (.app t₁ tβ‚‚')
/-- Congruence in the type annotation of an abstraction. -/
| abs₁ : tβ‚‚.body β†’ BetaEquiv t₁ t₁' β†’ BetaEquiv (.abs t₁ tβ‚‚) (.abs t₁' tβ‚‚)
/-- Congruence in the body of an abstraction. -/
| absβ‚‚ (ρ : Finset Var) :
t₁.LC β†’
(βˆ€ x βˆ‰ ρ, BetaEquiv (tβ‚‚ ^α΅— .fvar x) (tβ‚‚' ^α΅— .fvar x)) β†’
BetaEquiv (.abs t₁ tβ‚‚) (.abs t₁ tβ‚‚')
/-- Congruence in the domain of a pi type. -/
| pi₁ : tβ‚‚.body β†’ BetaEquiv t₁ t₁' β†’ BetaEquiv (.pi t₁ tβ‚‚) (.pi t₁' tβ‚‚)
/-- Congruence in the codomain of a pi type. -/
| piβ‚‚ (ρ : Finset Var) :
t₁.LC β†’
(βˆ€ x βˆ‰ ρ, BetaEquiv (tβ‚‚ ^α΅— .fvar x) (tβ‚‚' ^α΅— .fvar x)) β†’
BetaEquiv (.pi t₁ tβ‚‚) (.pi t₁ tβ‚‚')

instance instHasBetaEquivTerm : HasBetaEquiv (Term Var) where
BetaEquiv := BetaEquiv


variable [DecidableEq Var]

namespace Term

/-- Well-formedness of terms. -/
inductive Wf : Env Var β†’ Term Var β†’ Prop
| var : ⟨x, _⟩ ∈ Ξ“ β†’ Wf Ξ“ (fvar x)
| app : Wf Ξ“ f β†’ Wf Ξ“ a β†’ Wf Ξ“ (app f a)
| abs (L : Finset Var) :
Wf Ξ“ Οƒ β†’
(βˆ€ X βˆ‰ L, Wf ({⟨X,ΟƒβŸ©} βˆͺ Ξ“) (Ο„ ^α΅— fvar X)) β†’
Wf Ξ“ (abs Οƒ Ο„)
| pi (L : Finset Var) :
Wf Ξ“ Οƒ β†’
(βˆ€ X βˆ‰ L, Wf ({⟨X,ΟƒβŸ©} βˆͺ Ξ“) (Ο„ ^α΅— fvar X)) β†’
Wf Ξ“ (pi Οƒ Ο„)
| type : Wf Ξ“ (type _)

end Term

mutual

/-- An environment is well-formed if it binds each variable exactly once to a well-formed type. -/
inductive Env.Wf : Env Var β†’ Prop
| nil : Env.Wf {}
| cons : Env.Wf Ξ“ β†’ Typing Ξ“ Ο„ (.type i) β†’ x βˆ‰ Ξ“.dom β†’ Env.Wf ({⟨x, Ο„βŸ©} βˆͺ Ξ“)

/-- Typing judgement -/
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

end

end LambdaCalculus.LocallyNameless.Coc

end Cslib
14 changes: 14 additions & 0 deletions references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ @book{Baader1998
address = {USA}
}

@article{Coquand1996,
title = {An algorithm for type-checking dependent types},
journal = {Science of Computer Programming},
volume = {26},
number = {1},
pages = {167-177},
year = {1996},
issn = {0167-6423},
doi = {https://doi.org/10.1016/0167-6423(95)00021-6},
url = {https://www.sciencedirect.com/science/article/pii/0167642395000216},
author = {Thierry Coquand},
abstract = {We present a simple type-checker for a language with dependent types and let expressions, with a simple proof of correctness.}
}

@inproceedings{Danielsson2008,
author = {Danielsson, Nils Anders},
title = {Lightweight semiformal time complexity analysis for purely functional data structures},
Expand Down