Files
lean-pl-tutorials/tutorial-02-semantics/08-syntax-representation.org
Hermes Agent 6e2914b06e migrate all tutorials from Markdown to Org mode
Converted with pandoc 3.7 (markdown → org), all 17 files:
- README, references, 15 tutorial units
- Internal file links updated from .md to .org
- Source code blocks (#+begin_src lean) preserved
- Tables, math notation, links intact

For the Emacs workflow.
2026-05-28 20:15:38 +02:00

93 lines
2.9 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
* Unit 8 --- Representing Syntax
:PROPERTIES:
:CUSTOM_ID: unit-8-representing-syntax
:END:
*Tutorial 2: PL Semantics in Lean* · [[../README.org][← Back to README]]
** Goals
:PROPERTIES:
:CUSTOM_ID: goals
:END:
- Encode lambda calculus terms as an inductive type
- Understand three binding representations:
1. *Named* (strings --- simple, but α-equiv isn't definitional)
2. *de Bruijn indices* (numbers --- α-equiv is free, shifting is painful)
3. *Locally nameless* (free vars named, bound vars indexed --- compromise)
We'll use de Bruijn indices (the "heavy lifter") for the rest of this tutorial,
with locally nameless for comparison.
** Sources
:PROPERTIES:
:CUSTOM_ID: sources
:END:
- syndikos/lean4-stlc =Syntax.lean=: https://github.com/syndikos/lean4-stlc
- Chris Henson 2025: https://chrishenson.net/posts/2025-05-10-formalized_lambda_calculus.html
- chenson2018/LeanScratch: https://github.com/chenson2018/LeanScratch
- Software Foundations Vol.2: https://softwarefoundations.cis.upenn.edu/
** Exercises
:PROPERTIES:
:CUSTOM_ID: exercises
:END:
#+begin_src lean
-- 8.1 — Named representation
inductive NamedTerm where
| var (x : String)
| lam (x : String) (body : NamedTerm)
| app (f arg : NamedTerm)
deriving Repr
-- The Church encoding of identity: λx. x
def idNamed : NamedTerm := NamedTerm.lam "x" (NamedTerm.var "x")
-- Encode λx. λy. x (K combinator)
def kNamed : NamedTerm :=
sorry
-- Encode λf. λx. f (f x) (Church numeral 2)
def twoNamed : NamedTerm :=
sorry
-- 8.2 — de Bruijn representation
-- Variables are numbers: 0 = nearest binder, 1 = next, etc.
inductive DBTerm where
| var (idx : Nat) -- variable reference by binding distance
| lam (body : DBTerm) -- λ. body (no name needed!)
| app (f arg : DBTerm)
deriving Repr
-- λ. λ. 1 (= λx. λy. x in named form)
def kDB : DBTerm := DBTerm.lam (DBTerm.lam (DBTerm.var 1))
-- λ. 0 (= λx. x in named form)
def idDB : DBTerm := DBTerm.lam (DBTerm.var 0)
-- Encode λf. λx. f (f x) (Church 2)
def twoDB : DBTerm :=
sorry
-- 8.3 — Locally nameless
-- Free variables are strings, bound variables are de Bruijn indices
-- (You don't need to implement this fully — just understand the idea)
inductive LNTerm where
| fvar (x : String) -- free variable
| bvar (idx : Nat) -- bound variable (de Bruijn)
| lam (body : LNTerm) -- binder
| app (f arg : LNTerm)
deriving Repr
#+end_src
*** Key insight for PL semantics
:PROPERTIES:
:CUSTOM_ID: key-insight-for-pl-semantics
:END:
When we encode *typing contexts* =Γ = x₁:τ₁, x₂:τ₂, ...=, de Bruijn indices
give us "index into the context" for free. The last binding is index 0, the
second-last is index 1, etc. This makes the typing rules elegant in Lean ---
no name-clash avoidance needed.
--------------
← [[../tutorial-01-basics/07-dependent-types.org][Tutorial 1 --- Unit 7]] · Next: [[file:09-substitution.org][Unit 9 --- Substitution]]