Initial commit: 15-unit Lean 4 + PL semantics tutorial curriculum

This commit is contained in:
2026-05-28 18:14:07 +02:00
commit 0cf85517c2
17 changed files with 1454 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
# Unit 1 — Types and Functions
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Define functions with `def` and lambda notation
- Understand Lean's basic types: `Nat`, `Int`, `String`, `Bool`, `List`, `Option`
- Use pattern matching and `match` expressions
- Run code with `#eval`
## Source
*Functional Programming in Lean* (FPIL), Chapters 12
→ https://lean-lang.org/functional_programming_in_lean/
## Concepts
| Concept | Lean syntax | Example |
|---------|------------|---------|
| Function definition | `def f (x : Nat) : Nat :=` | `def double (x : Nat) : Nat := x + x` |
| Lambda | `fun x => ...` | `fun x => x + 1` |
| Pattern matching | `match e with \| pat => ...` | `match n with \| 0 => "zero" \| _ => "non-zero"` |
| Recursion | `def f ... := match ...` | Structural recursion only (no general fixpoint) |
| `#eval` | `#eval expr` | `#eval double 21` prints `42` |
## Exercises
> **How to work**: Create a file `Unit1.lean`. Write each exercise, then
> `#eval` to test it (if it's a function) or just make it compile (if it's a
> proof or definition). Fill in the `sorry` placeholders.
### Exercise 1.1 — Warm-up functions
```lean
-- (a) Write a function that returns the absolute difference between two nats
def absDiff (a b : Nat) : Nat :=
sorry
#eval absDiff 7 3 -- expected: 4
#eval absDiff 3 7 -- expected: 4
#eval absDiff 5 5 -- expected: 0
-- (b) Write a function that checks whether a list contains an element
def contains [BEq α] (x : α) (xs : List α) : Bool :=
sorry
#eval contains 3 [1, 2, 3, 4] -- expected: true
#eval contains 5 [1, 2, 3, 4] -- expected: false
-- (c) Write `map` (without looking at the standard library)
def myMap (f : α β) (xs : List α) : List β :=
sorry
#eval myMap (fun x => x * 2) [1, 2, 3] -- expected: [2, 4, 6]
```
### Exercise 1.2 — Recursion on lists
```lean
-- (a) Sum of a list
def sumList (xs : List Nat) : Nat :=
sorry
#eval sumList [1, 2, 3, 4] -- expected: 10
-- (b) Reverse a list (the slow way is fine)
def myReverse (xs : List α) : List α :=
sorry
#eval myReverse [1, 2, 3] -- expected: [3, 2, 1]
-- (c) Filter: keep elements satisfying p
def myFilter (p : α Bool) (xs : List α) : List α :=
sorry
#eval myFilter (fun x => x % 2 == 0) [1, 2, 3, 4, 5, 6] -- expected: [2, 4, 6]
```
### Exercise 1.3 — Option type
```lean
-- (a) Safe head
def safeHead (xs : List α) : Option α :=
sorry
#eval safeHead ([1, 2, 3] : List Nat) -- expected: some 1
#eval safeHead ([] : List Nat) -- expected: none
-- (b) Safe division
def safeDiv (a b : Nat) : Option Nat :=
sorry
#eval safeDiv 10 2 -- expected: some 5
#eval safeDiv 10 0 -- expected: none
-- (c) Lookup in an association list
def lookup [BEq α] (key : α) (alist : List (α × β)) : Option β :=
sorry
#eval lookup "b" [("a", 1), ("b", 2), ("c", 3)] -- expected: some 2
#eval lookup "d" [("a", 1), ("b", 2), ("c", 3)] -- expected: none
```
---
← [Back to README](../README.md) · Next: [Unit 2 — Inductive Types](02-inductive-types.md)

View File

@@ -0,0 +1,106 @@
# Unit 2 — Inductive Types
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Define inductive data types with the `inductive` keyword
- Understand constructors and pattern matching
- Write structurally recursive functions over inductive types
- Define `Nat`, `List`, and `Tree` from scratch
## Sources
- *Theorem Proving in Lean 4* (TPIL), Chapter 7 "Inductive Types"
→ https://leanprover.github.io/theorem_proving_in_lean4/
- *Functional Programming in Lean* (FPIL), Chapter 1
## Concepts
| Concept | Lean syntax |
|---------|------------|
| Inductive definition | `inductive Name where \| ctor1 : ... → Name` |
| Pattern matching constructors | `match t with \| ctor1 x => ... \| ctor2 y => ...` |
| Structural recursion | Recursion on *structurally smaller* subterms only |
| `deriving` clauses | `deriving Repr, BEq, DecidableEq` for auto-generated code |
## Exercises
### Exercise 2.1 — Define `Nat` from scratch
```lean
inductive MyNat where
| zero : MyNat
| succ : MyNat MyNat
deriving Repr
-- (a) Addition
def myAdd (a b : MyNat) : MyNat :=
sorry
-- Test (after defining conversion functions or writing your own checks)
-- (b) Multiplication
def myMul (a b : MyNat) : MyNat :=
sorry
-- (c) Prove that zero is a right identity: myAdd a MyNat.zero = a
-- (We'll do proofs properly in Unit 3, but try `by rfl` for now)
```
### Exercise 2.2 — Define `List α` from scratch
```lean
inductive MyList (α : Type) where
| nil : MyList α
| cons : α MyList α MyList α
deriving Repr
-- (a) Length
def myLength {α : Type} (xs : MyList α) : Nat :=
sorry
-- (b) Append
def myAppend {α : Type} (xs ys : MyList α) : MyList α :=
sorry
-- (c) Prove: myLength (myAppend xs ys) = myLength xs + myLength ys
-- (Try this after Unit 4/5)
```
### Exercise 2.3 — Binary trees
```lean
inductive BinTree (α : Type) where
| leaf : BinTree α
| node : BinTree α α BinTree α BinTree α
deriving Repr
-- (a) Count the number of nodes
def size {α : Type} (t : BinTree α) : Nat :=
sorry
-- Example tree: node (node leaf 1 leaf) 2 (node leaf 3 leaf)
def exampleTree : BinTree Nat :=
BinTree.node (BinTree.node BinTree.leaf 1 BinTree.leaf) 2
(BinTree.node BinTree.leaf 3 BinTree.leaf)
#eval size exampleTree -- expected: 3
-- (b) Collect all values into a list (inorder traversal)
def inorder {α : Type} (t : BinTree α) : List α :=
sorry
#eval inorder exampleTree -- expected: [1, 2, 3]
-- (c) Check if a tree is a binary search tree
-- Assume Int keys. A BST satisfies: left < key < right at every node.
-- Write a function that checks this property.
-- Hint: you may want a helper with min/max bounds.
def isBST (t : BinTree Int) : Bool :=
sorry
```
---
← [Previous: Unit 1](01-types-and-functions.md) · Next: [Unit 3 — Propositions and Proofs](03-propositions-and-proofs.md)

View File

@@ -0,0 +1,90 @@
# Unit 3 — Propositions and Proofs
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Understand propositions as types (Curry-Howard)
- Write basic proofs using `example` and `theorem`
- Use the tactic language: `intro`, `apply`, `exact`, `have`
## Source
*Theorem Proving in Lean 4* (TPIL), Chapters 23
→ https://leanprover.github.io/theorem_proving_in_lean4/
## Concepts
| Concept | Lean |
|---------|------|
| Proposition | `P : Prop` — a type whose terms are proofs |
| Implication `P → Q` | Function from proofs of `P` to proofs of `Q` |
| Conjunction `P ∧ Q` | `And.intro` (constructor), `.left` / `.right` (projections) |
| Disjunction `P Q` | `Or.inl` / `Or.inr` |
| Negation `¬ P` | `P → False` |
| Tactics | `intro`, `apply`, `exact`, `have`, `assumption` |
## Exercises
### Exercise 3.1 — Implicational logic
```lean
-- (a) Identity
theorem id_prop (A : Prop) : A A :=
by
intro h
exact h
-- (b) Composition
theorem compose (A B C : Prop) : (A B) (B C) (A C) :=
by
sorry
-- (c) Currying
theorem curry (A B C : Prop) : (A B C) (A B C) :=
by
sorry
```
### Exercise 3.2 — Conjunction and disjunction
```lean
-- (a) Swap conjuncts
theorem and_comm (A B : Prop) : A B B A :=
by
sorry
-- (b) Disjunction is symmetric
theorem or_comm (A B : Prop) : A B B A :=
by
sorry
-- (c) Forward reasoning with `have`
theorem forward_example (A B C : Prop) (h1 : A B) (h2 : B C) (ha : A) : C :=
by
have hb : B := h1 ha
sorry
```
### Exercise 3.3 — Negation
```lean
-- (a) Contradiction
theorem contrapositive (A B : Prop) : (A B) (¬ B ¬ A) :=
by
sorry
-- (b) Double negation introduction
theorem double_neg_intro (A : Prop) : A ¬ ¬ A :=
by
sorry
-- (c) Ex falso quodlibet
theorem ex_falso (A : Prop) : False A :=
by
sorry
```
---
← [Previous: Unit 2](02-inductive-types.md) · Next: [Unit 4 — Quantifiers and Equality](04-quantifiers-and-equality.md)

View File

@@ -0,0 +1,97 @@
# Unit 4 — Quantifiers and Equality
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Use `∀` (forall) and `∃` (exists) quantifiers
- Manipulate equality with `rfl`, `rw`, `calc`
- Combine quantifiers with logical connectives
## Source
*Theorem Proving in Lean 4* (TPIL), Chapter 4
→ https://leanprover.github.io/theorem_proving_in_lean4/
## Concepts
| Concept | Lean |
|---------|------|
| Universal `∀ x, P x` | `intro x` to prove; `h x` or `apply h` to use |
| Existential `∃ x, P x` | `⟨x, h⟩` to prove; `rcases h with ⟨x, hx⟩` to use |
| Equality `a = b` | `rfl` when definitional; `rw [h]` when propositional |
| `calc` block | Chain equalities: `calc a = b := h1; _ = c := h2` |
## Exercises
### Exercise 4.1 — Universal quantifier
```lean
-- (a) Prove a simple forall
theorem all_imp (P Q : Nat Prop) (h : n, P n Q n) (x : Nat) (hp : P x) : Q x :=
by
sorry
-- (b) Distributing ∀ over ∧
theorem forall_and_distrib (P Q : Nat Prop) : ( n, P n Q n) ( n, P n) ( n, Q n) :=
by
constructor
· sorry -- left-to-right
· sorry -- right-to-left
-- (c) Prove that if every natural is even, then 42 is even
-- (Use: `Even n := ∃ k, n = 2*k`)
def Even (n : Nat) : Prop := k, n = 2 * k
theorem all_even_implies_42 : ( n, Even n) Even 42 :=
by
sorry
```
### Exercise 4.2 — Existential quantifier
```lean
-- (a) Prove existence
theorem exists_square (a : Nat) : n, n = a * a :=
by
sorry
-- (b) Distributing ∃ over
theorem exists_or_distrib (P Q : Nat Prop) : ( n, P n Q n) ( n, P n) ( n, Q n) :=
by
constructor
· sorry
· sorry
-- (c) Prove: if something is both even and greater than 10, then something is greater than 5
theorem even_and_gt10_implies_gt5 : ( n, Even n n > 10) ( n, n > 5) :=
by
sorry
```
### Exercise 4.3 — Equality and rewriting
```lean
-- (a) Prove symmetry and transitivity of equality (without using `symm`/`trans`)
theorem eq_symm {α : Type} (a b : α) (h : a = b) : b = a :=
by
sorry
theorem eq_trans {α : Type} (a b c : α) (h1 : a = b) (h2 : b = c) : a = c :=
by
sorry
-- (b) Use `calc` to prove a chain of identities
theorem calc_example (a b c d : Nat) (h1 : a = b) (h2 : b = c + 1) (h3 : c + 1 = d) : a = d :=
by
sorry
-- (c) Rewrite inside a goal
theorem rewrite_example (a b : Nat) (h : a = b + 1) : a * 2 = (b + 1) * 2 :=
by
sorry
```
---
← [Previous: Unit 3](03-propositions-and-proofs.md) · Next: [Unit 5 — Advanced Tactics](05-advanced-tactics.md)

View File

@@ -0,0 +1,52 @@
# Unit 5 — Advanced Tactics
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Prove properties by induction with `induction`
- Use `simp`, `ring`, `omega` for automation
- Use `rcases` and `obtain` for case analysis
- Work with `Nat` arithmetic proofs
## Sources
- TPIL Chapters 56 → https://leanprover.github.io/theorem_proving_in_lean4/
- *Mathematics in Lean* (MiL), Chapter 1 → https://leanprover-community.github.io/mathematics_in_lean/
## Exercises
```lean
open Nat
-- 5.1 — Induction on naturals
theorem add_assoc (a b c : Nat) : (a + b) + c = a + (b + c) := by
sorry
theorem add_comm (a b : Nat) : a + b = b + a := by
sorry
theorem mul_comm (a b : Nat) : a * b = b * a := by
sorry
theorem zero_mul (n : Nat) : 0 * n = 0 := by
sorry
-- 5.2 — Induction on lists
theorem reverse_reverse (xs : List α) : xs.reverse.reverse = xs := by
sorry
theorem length_append (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by
sorry
theorem map_id (xs : List α) : xs.map id = xs := by
sorry
-- 5.3 — Using `simp` and `ring`
theorem square_sum (a b : Nat) : (a + b) ^ 2 = a ^ 2 + 2 * a * b + b ^ 2 := by
sorry
```
---
← [Previous: Unit 4](04-quantifiers-and-equality.md) · Next: [Unit 6 — Structures and Type Classes](06-structures-type-classes.md)

View File

@@ -0,0 +1,68 @@
# Unit 6 — Structures and Type Classes
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Define `structure` for records with named fields
- Understand type classes as structures + instance resolution
- Use `deriving` for `Repr`, `BEq`, `Inhabited`
- Write monadic code with `do` notation
## Sources
- TPIL Chapter 9 (Structures), Chapter 10 (Type Classes)
- FPIL Chapters 35 (overloading, monads)
## Exercises
```lean
-- 6.1 — Structures
structure Point where
x : Float
y : Float
deriving Repr
def distance (p q : Point) : Float :=
sorry
#eval distance {x := 0, y := 0} {x := 3, y := 4} -- expected: 5.0
-- 6.2 — Type classes
class Semigroup (α : Type) where
mul : α α α
mul_assoc : a b c : α, mul (mul a b) c = mul a (mul b c)
instance : Semigroup Nat where
mul := Nat.mul
mul_assoc := Nat.mul_assoc
-- Define Monoid extending Semigroup with an identity
class Monoid (α : Type) extends Semigroup α where
one : α
mul_one : a : α, mul a one = a
one_mul : a : α, mul one a = a
instance : Monoid Nat where
one := 1
mul_one := sorry
one_mul := sorry
-- 6.3 — Option monad and `do` notation
def safeDiv (a b : Nat) : Option Nat :=
if b == 0 then none else some (a / b)
-- Rewrite this with `do` notation:
def compute (x y z : Nat) : Option Nat :=
do
let a safeDiv x y
let b safeDiv a z
some (b + 1)
#eval compute 10 2 3 -- expected: some 2
#eval compute 10 0 3 -- expected: none
```
---
← [Previous: Unit 5](05-advanced-tactics.md) · Next: [Unit 7 — Dependent Types](07-dependent-types.md)

View File

@@ -0,0 +1,77 @@
# Unit 7 — Dependent Types
**Tutorial 1: Lean 4 Fundamentals** · [← Back to README](../README.md)
## Goals
- Understand types that depend on values
- Work with `Fin n` (numbers < n), `Vector α n` (fixed-length lists)
- Write functions whose return type depends on input
- See why dependent types matter for PL semantics
## Sources
- TPIL Chapter 2 "Dependent Type Theory"
- FPIL Chapter 7 "Programming with Dependent Types"
- Henson 2025: "Beginner Resources for Formalizing Lambda Calculi"
## Concepts
| Concept | Lean |
|---------|------|
| Pi type `(x : α) → β x` | Function where return type depends on argument |
| Sigma type `Σ x : α, β x` | Dependent pair: value + property |
| `Fin n` | Natural numbers `< n` |
| `Vector α n` | Lists with length tracked in the type |
| `h : a = b → ...` | Equality can rewrite types (substitution principle) |
## Exercises
```lean
-- 7.1 — Fin n
-- Define a safe nth function for lists that *cannot* fail at runtime
def safeNth {α : Type} (xs : List α) (i : Fin xs.length) : α :=
sorry
-- (You'll need `match` on both `xs` and `i`)
-- Test: `safeNth [1,2,3] ⟨0, by decide⟩` should return 1
-- 7.2 — Vector
inductive Vector (α : Type) : Nat Type where
| nil : Vector α 0
| cons : α {n : Nat} Vector α n Vector α (n+1)
deriving Repr
-- (a) Append two vectors (length is m + n)
def vappend {α : Type} {m n : Nat} (v : Vector α m) (w : Vector α n) : Vector α (m + n) :=
sorry
-- (b) Map over a vector
def vmap {α β : Type} {n : Nat} (f : α β) (v : Vector α n) : Vector β n :=
sorry
-- 7.3 — Dependent types for ASTs
-- Define an AST where each expression *carries its type*
inductive Expr : Type where
| lit : Nat Expr
| add : Expr Expr Expr
| isZero : Expr Expr
deriving Repr
-- Now define the *typing predicate* as a dependent relation
-- (Preview of Tutorial 2)
inductive Ty : Type where | nat : Ty | bool : Ty
-- Fill this in: `HasType e τ` means "expression e has type τ"
inductive HasType : Expr Ty Prop where
-- (your constructors here)
-- Then prove: every well-typed `isZero` expression returns a bool
theorem isZero_returns_bool (e : Expr) : HasType (Expr.isZero e) Ty.nat HasType e Ty.nat :=
by
sorry
```
---
← [Previous: Unit 6](06-structures-type-classes.md) · [← Tutorial 1 Index](./) · Next: [Tutorial 2 — Unit 8](../tutorial-02-semantics/08-syntax-representation.md)