Skip to content

feat: de Bruijn Syntax for Untyped Lambda Calculus and a proof of Church-Rosser with Parallel Reduction#475

Open
zayn7lie wants to merge 13 commits intoleanprover:mainfrom
zayn7lie:main
Open

feat: de Bruijn Syntax for Untyped Lambda Calculus and a proof of Church-Rosser with Parallel Reduction#475
zayn7lie wants to merge 13 commits intoleanprover:mainfrom
zayn7lie:main

Conversation

@zayn7lie
Copy link
Copy Markdown

@zayn7lie zayn7lie commented Apr 7, 2026

Main contributions

Q1308502: Church-Rosser theorem in 1000 theorems

  • A de Bruijn syntax for λ-terms together with substitution and shifting operations.
  • Definition of one-step β-reduction.
  • Definition of parallel β-reduction (Par) and its basic properties.
  • Abstract confluence infrastructure (Diamond, Confluent) for binary relations.
  • Proof that parallel β-reduction satisfies the diamond property.
  • Deduction of confluence of β-reduction (Church–Rosser theorem) via standard closure arguments.

The development follows the classical proof strategy:
parallel reduction -> diamond -> confluence -> Church–Rosser.

Design choices

  • De Bruijn indices are used to avoid α-equivalence bookkeeping.
  • Parallel reduction is defined inductively to enable a direct diamond proof.
  • Confluence lemmas are factored through generic relation-theoretic definitions
    (Diamond, Confluent) to keep the proof modular.

Use of AI

  • Rough structure for beginning
  • Small/trivial structure proof
  • Docs/Comment generating
  • Final refinement

@chenson2018 chenson2018 self-assigned this Apr 7, 2026
Copy link
Copy Markdown
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.

Hi, thanks for your interest in contributing to CSLib! We do have Church-Rosser for locally nameless binding, but I think we are interested in having the more common de Bruijn indices representation as well.

I see there is a disclosure of AI usage, but this looks like its been mostly cleaned up pretty well, so thank you.

Below are some initial reviews about making this fit into CSLib conventions.

Comment thread Cslib/Languages/LambdaCalculus/Unscoped/Untyped/DeBruijnSyntax.lean
Comment thread Cslib/Languages/LambdaCalculus/Unscoped/Untyped/ConfluentReduction.lean Outdated
Comment thread Cslib/Computability/LambdaCalculus/DeBruijnSyntax.lean Outdated
Comment thread Cslib/Computability/LambdaCalculus/DeBruijnSyntax.lean Outdated
| app t₁ t₂ ih₁ ih₂ => simp_all

/-- Communitivity of incre. -/
public theorem incre_comm {i j k l t} :
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can clean up some of the arithmetic in theorems like this, but I'll wait until we have more high-level changes done first to give specific suggestions.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Sure, thanks.

Comment thread Cslib/Computability/LambdaCalculus/DeBruijnSyntax.lean Outdated
| appL {t t' u} : Beta t t' → Beta (t·u) (t'·u)
| appR {t u u'} : Beta u u' → Beta (t·u) (t·u')
| red (t' s : Term) : Beta ((λ.t')·s) (t'.sub 0 s)
public abbrev BetaStar := Relation.ReflTransGen Beta
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We use the attribute reduction_sys for generating notations for reductions and the multi-step closure.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

At the same time, would you know how to type in Lean for single reduction?

Comment thread Cslib/Languages/LambdaCalculus/Unscoped/Untyped/BetaReduction.lean Outdated
@zayn7lie zayn7lie changed the title feat: Church–Rosser theorem (Q1308502) for ULC (de Bruijn) feat: de Bruijn Syntax for Untyped Lambda Calculus and a proof of Church-Rosser with Parallel Reduction Apr 10, 2026
@zayn7lie zayn7lie requested a review from chenson2018 April 11, 2026 23:58
@zayn7lie
Copy link
Copy Markdown
Author

@zayn7lie Please rebase on upstream rather than merge

Thanks for notification, sorry for confusion.

@chenson2018
Copy link
Copy Markdown
Collaborator

sub looks too complicated, compared to https://api.cslib.io/docs/Cslib/Languages/LambdaCalculus/LocallyNameless/Untyped/Basic.html#Cslib.LambdaCalculus.LocallyNameless.Untyped.Term.openRec

I don't understand this comment. It looks as I would expect for this binding scheme.

@zayn7lie
Copy link
Copy Markdown
Author

Compared to https://api.cslib.io/docs/Cslib/Languages/LambdaCalculus/LocallyNameless/Untyped/Basic.html Some new definition are introduced: incre decre

Are they really necessary ?

Please read the header comment docs of DeBruijnSyntax.lean. I have also referred to https://en.wikipedia.org/wiki/De_Bruijn_index in it. Formal definition has the description for primitive operation (substitution).

@lengyijun
Copy link
Copy Markdown
Contributor

Can you rename sub to subst, according to proof of locally nameless

@zayn7lie
Copy link
Copy Markdown
Author

Can you rename sub to subst, according to proof of locally nameless

I am somehow hesitant to do that. The current subst is more primitive than sub (sub is already very specific for beta reduction), aligning with incre and decre. And I have also introduced Cslib.HasSubstitution, so we might not need to do anything with sub later, but using t[n:=s]. subst might be more useful for future definition of other kinds of usage. @chenson2018 What would you think about this?

@chenson2018
Copy link
Copy Markdown
Collaborator

I agree that we should almost always use the notation and corresponding naming, which is then based off HasSubstitution.subst. So things using the notation should probably use subst, but I'm not sure I care about the name used in the instance.

I've started a second pass of reviews which I'll try to have complete in a few days.

Comment on lines +56 to +58
(h₁ : ∀ {a b}, r a b → p a b)
(h₂ : ∀ {a b}, p a b → ReflTransGen r a b) :
∀ {a b}, ReflTransGen r a b ↔ ReflTransGen p a b := by
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Perhaps better as

Suggested change
(h₁ : ∀ {a b}, r a b → p a b)
(h₂ : ∀ {a b}, p a b → ReflTransGen r a b) :
∀ {a b}, ReflTransGen r a b ↔ ReflTransGen p a b := by
(h₁ : r ≤ p)
(h₂ : p ≤ ReflTransGen r) :
ReflTransGen r = ReflTransGen p := by

I think this was discussed on Zulip?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes, this thread. I'd use the naming and shorter proof discussed there as well.

Comment on lines +103 to +107
| Par.var t => cases em (t = n) with
| inl h =>
simp_all only [sub, subst, ↓reduceIte,
decre_incre_elim, incre_par]
| inr h => cases em (t < n) with
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think this is better handled with obtain h | h | h := lt_trichotomy t n

Comment on lines +56 to +58
notation:max "𝕧" str => Term.var str
notation:max "λ." t => Term.abs t
infixl:77 "·" => Term.app
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These should use @[inherit_doc]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The application notation is also problematic, as it conflicts the core \. notation.

Comment on lines +66 to +80
/-- `subst j s t` substitutes `j` with term `s` in `t`. -/
@[expose] public def subst (j : Nat) (s : Term) : Term → Term
| var k => if k = j then s else var k
| abs t => abs (subst (j + 1) (incre 1 0 s) t)
| app t u => app (subst j s t) (subst j s u)

/-- `decre i l t` decrements `i` for all free vars `≥ l + i`.
the reason using `l + i` is that it is more explicit for
free variable elimination. For example, after eliminating
`var k` for Term `t` from the most outside, `decre 1 k t`
will close the gap caused by `k` elimination. -/
@[expose] public def decre (i : Nat) (l : Nat) : Term → Term
| var k => if l + i ≤ k then var (k - i) else var k
| abs t => abs (decre i (l + 1) t)
| app t u => app (decre i l t) (decre i l u)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should this be a single operation?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hmm, I don't think I've ever seen it done that way before? It might be awkward not having incre and decre as inverses. I'm not sure though.

| app t u => app (decre i l t) (decre i l u)

/-- Substitute into the body of a lambda: `(λ.t) s` -/
@[expose] public def sub (t : Term) (n : Nat) (s : Term) :=
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
@[expose] public def sub (t : Term) (n : Nat) (s : Term) :=
@[expose] public def sub (t : Term) (n : Nat) (s : Term) : Term :=

Comment on lines +48 to +50
| var : Nat → Term
| abs : Term → Term
| app : Term → Term → Term
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please write a docstring for each of these

Copy link
Copy Markdown
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.

This is still not comprehensive, but a few more comments. The comment about squeezing terminal simp applies throughout and is one factor making proofs much longer than they need to be. Could you work on fixing this a bit?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's follow the convention of naming this file Basic.lean.

-/


namespace Lambda
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The namespacing should closely follow the previous lamba calculi developments. I think that would make this Cslib.LambdaCalculus.Unscoped.Untyped.

Comment on lines +53 to +54
open Term
namespace Term
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It is redundant to both open and then enter a namespace

Suggested change
open Term
namespace Term
namespace Term

infixl:77 "·" => Term.app

/-- `incre i l t` increments `i` for all free vars `≥ l`. -/
@[expose] public def incre (i : Nat) (l : Nat) : Term → Term
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As I mentioned previously, I would remove everywhere you use public and @[expose] in favor of @[expose] public section

Comment on lines +93 to +96
induction t generalizing l with
| var k => simp_all only [incre, Nat.add_zero, ite_self]
| abs t ih => simp_all only [incre]
| app t u iht ihu => simp_all only [incre]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These simp are terminal, so do not need to be squeezed. Personally I would just write

Suggested change
induction t generalizing l with
| var k => simp_all only [incre, Nat.add_zero, ite_self]
| abs t ih => simp_all only [incre]
| app t u iht ihu => simp_all only [incre]
induction t generalizing l with grind [incre]

Comment on lines +56 to +58
notation:max "𝕧" str => Term.var str
notation:max "λ." t => Term.abs t
infixl:77 "·" => Term.app
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The application notation is also problematic, as it conflicts the core \. notation.

Comment on lines +56 to +58
(h₁ : ∀ {a b}, r a b → p a b)
(h₂ : ∀ {a b}, p a b → ReflTransGen r a b) :
∀ {a b}, ReflTransGen r a b ↔ ReflTransGen p a b := by
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes, this thread. I'd use the naming and shorter proof discussed there as well.

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.

4 participants