Skip to content

Commit 158fd80

Browse files
committed
added lean as programming language section
1 parent 726d38a commit 158fd80

3 files changed

Lines changed: 353 additions & 0 deletions

File tree

LeanBlockCourse26/P01_Introduction/S04_NumberTheoryExample.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Mathlib.Algebra.BigOperators.Fin
22
import Mathlib.Data.Nat.Prime.Basic
33

4+
set_option linter.style.emptyLine false
5+
46
/-
57
# An example from number theory
68
=====================

LeanBlockCourse26/P01_Introduction/S04_TopologyExample.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Mathlib.Topology.Basic
22
import Mathlib.Tactic.Basic
33

4+
set_option linter.style.emptyLine false
5+
46
/-
57
# An example from topology
68
=====================
Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
import Mathlib.Tactic.Basic
2+
3+
set_option linter.style.emptyLine false
4+
5+
/-
6+
# Introduction to Lean as a Programming Language
7+
=====================
8+
9+
## Basic Values and Printing
10+
In Lean, we declare values using `def`. The type can be inferred or explicitly stated.
11+
-/
12+
13+
-- Basic Hello World
14+
def hello : String := "Hello, World!"
15+
#check hello
16+
17+
-- Types can be infered
18+
def hello2 := "Hello, World!"
19+
#check hello2
20+
21+
/-
22+
Compare with:
23+
Python: message = "Hello, World!" # Dynamic typing
24+
C: const char* hello = "Hello, World!"; // Static typing
25+
-/
26+
27+
def printHello : IO Unit := -- 'IO Unit' is an Explicit type for IO operations
28+
IO.println hello
29+
30+
#check printHello -- tells us this is of type 'IO Unit'
31+
#eval printHello -- actually executes the method and prints "Hello, World!"
32+
33+
34+
/-
35+
## Basic Arithmetic
36+
Lean uses natural numbers (Nat) by default for integers. Functions can be defined
37+
with explicit type annotations, similar to C but with a different syntax.
38+
-/
39+
40+
def add (x y : Nat) : Nat := x + y
41+
42+
/-
43+
Compare with:
44+
Python: def add(x, y): return x + y
45+
C: int add(int x, int y) { return x + y; }
46+
-/
47+
48+
#eval add 2 3 -- 2 + 3 = 5
49+
#check add -- 'add' has type Nat → Nat → Nat
50+
#check add 2 3 -- 2 + 3 = 5 is of type Nat
51+
#check add 2 -- we can partially apply: 'add 2' is of type Nat → Nat
52+
53+
def triple_multiply (x y z : Nat) : Nat := x * y * z
54+
55+
#eval triple_multiply 1 2 3 -- 1 * 2 * 3 = 6
56+
#check triple_multiply 1 2 3 -- 1 * 2 * 3 = 6 is of type Nat
57+
#check triple_multiply -- 'triple_multiply' is of type Nat → Nat → Nat → Nat
58+
59+
60+
/-
61+
## Pattern Matching and Control Flow
62+
Lean uses pattern matching as its primary control flow mechanism. This is more
63+
powerful than traditional if/switch statements found in C or Python.
64+
-/
65+
66+
def calculator (op : String) (x y : Nat) : Nat :=
67+
match op with
68+
| "+" => x + y
69+
| "-" => x - y
70+
| "*" => x * y
71+
| _ => 0 -- default
72+
73+
/-
74+
Compare with:
75+
Python:
76+
def calculator(op, x, y):
77+
if op == "+": return x + y
78+
elif op == "-": return x - y
79+
elif op == "*": return x * y
80+
else: return 0
81+
82+
C:
83+
int calculator(char* op, int x, int y) {
84+
if (strcmp(op, "+") == 0) return x + y;
85+
else if (strcmp(op, "-") == 0) return x - y;
86+
else if (strcmp(op, "*") == 0) return x * y;
87+
else return 0;
88+
}
89+
-/
90+
91+
#eval calculator "*" 2 3 -- 2 * 3 = 6
92+
#check calculator -- 'calculator' is of type String → Nat → Nat → Nat
93+
94+
95+
/-
96+
## String Interpolation
97+
Lean provides string interpolation similar to modern programming languages.
98+
-/
99+
100+
def greeting (name : String) : String :=
101+
s!"Hello, {name}!"
102+
103+
/-
104+
Compare with:
105+
Python:
106+
def greeting(name):
107+
return f"Hello, {name}!"
108+
-/
109+
110+
#eval greeting "Martin" -- "Hello, Martin!"
111+
#check greeting -- 'greeting' is of type String → String
112+
#check greeting "Martin" -- 'greeting "Martin"' is of type String
113+
114+
115+
/-
116+
## Type Inference
117+
Lean has a powerful type inference system that can automatically determine types in many cases.
118+
This makes code more concise while maintaining type safety. The compiler will infer the most
119+
general type that satisfies the constraints.
120+
-/
121+
122+
-- Type inference for simple values
123+
def inferredNumber := 42 -- Inferred as Nat
124+
def inferredText := "Hello" -- Inferred as String
125+
def inferredList := [1, 2, 3] -- Inferred as List Nat
126+
127+
-- def mixedList := [1, "test"] -- Fails because List cannot have mixed type elements
128+
129+
#check inferredList
130+
131+
132+
-- Type inference for functions
133+
def inferredAdd (x : Nat) y := x + y -- type of `y` and of output is inferred as `Nat`
134+
def inferredConcat (x : String) y := x ++ y -- type of `y` and output is inferred as `String`
135+
136+
-- Sometimes explicit types are clearer or necessary
137+
def explicitSubNat (x y : Nat) := x - y -- Forces `Nat` arithmetic
138+
#check explicitSubNat -- Nat → Nat → Nat
139+
#check explicitSubNat 2 3 -- Nat
140+
#eval explicitSubNat 2 3 -- 2 - 3 = 0 in Nat
141+
142+
def explicitSubInt (x y : Int) := x - y
143+
#check explicitSubInt 2 3 -- Int
144+
#eval explicitSubInt 2 3 -- 2 - 3 = -1 in Int
145+
146+
-- def implictSub (x y) := x - y -- unable to infer type
147+
-- def implictSub (x y) : Int := x - y -- unable to infer type
148+
def implictSub (x : Int) y := x - y -- able to infer Int for y and output
149+
def implictSub' x (y : Int) := x - y -- able to infer Int for x and output
150+
151+
#check implictSub'
152+
#eval implictSub' 2 3
153+
154+
/-
155+
Compare with:
156+
Python: Type hints are optional
157+
def add(x, y): # No types needed
158+
return x + y
159+
160+
TypeScript: Type inference with explicit options
161+
let inferredNumber = 42; // number
162+
let explicitNumber: number = 42;
163+
-/
164+
165+
/-
166+
## Type Coercion
167+
Some types can be coerced into other types, like Nat to Int.
168+
-/
169+
170+
def implictSub'' (x : Nat) (y : Int) := x - y -- able to coerce y into Int and output
171+
172+
#check implictSub''
173+
#eval implictSub'' 2 3
174+
175+
def implictSub''' (x : Int) (y : Nat) := x - y -- able to coerce y to Int
176+
177+
#check implictSub'''
178+
#eval implictSub''' 2 3
179+
180+
def inferredAdd' (x : Nat) (y : Int) := x + y
181+
182+
def coercedOutputAdd (x y : Nat) : Int := x - y
183+
184+
#check coercedOutputAdd 2 3 -- Nat → Nat → Int, but it uses the
185+
-- Int subtraction and coereces the Nat to Int
186+
#eval coercedOutputAdd 2 3 -- 2 - 3 = -1 since x and y are both first coerced to Int
187+
188+
/-
189+
## Data Structures
190+
Lean provides several ways to structure data. Here we demonstrate:
191+
1. Simple structures (similar to C structs or Python classes)
192+
2. Namespace organization
193+
3. Method-like function definitions
194+
-/
195+
196+
structure Rectangle where
197+
width : Float
198+
height : Float
199+
deriving Repr
200+
201+
def myRectangle : Rectangle := { width := 4.0, height := 2.0 }
202+
203+
def Rectangle.area (r : Rectangle) : Float :=
204+
r.width * r.height
205+
206+
#eval Rectangle.area myRectangle
207+
#eval myRectangle.area
208+
209+
def Rectangle.perimeter (r : Rectangle) :=
210+
2.0 * (r.width + r.height)
211+
212+
#eval myRectangle.perimeter
213+
214+
structure Point where
215+
x : Float
216+
y : Float
217+
deriving Repr
218+
219+
/-
220+
Compare with:
221+
Python:
222+
class Point:
223+
def __init__(self, x, y):
224+
self.x = x
225+
self.y = y
226+
227+
C:
228+
struct Point {
229+
double x;
230+
double y;
231+
};
232+
-/
233+
234+
structure Circle where
235+
center : Point
236+
radius : Float
237+
deriving Repr
238+
239+
def π : Float := 3.14159265358979323846 -- don't do this!!
240+
241+
-- Instead of 'Rectangle.' we can also use 'namespace'
242+
namespace Circle
243+
244+
-- putting this into the namespace has the same effect
245+
-- as naming it Circle.area
246+
def area (c : Circle) : Float :=
247+
π * c.radius * c.radius
248+
249+
def circumference (c : Circle) : Float :=
250+
2.0 * π * c.radius
251+
252+
def containsPoint (c : Circle) (p : Point) : Bool :=
253+
let dx := c.center.x - p.x
254+
let dy := c.center.y - p.y
255+
dx * dx + dy * dy ≤ c.radius * c.radius
256+
257+
end Circle
258+
259+
def myCircle : Circle := {
260+
center := { x := 1.0, y := 1.0 }
261+
radius := 2.5
262+
}
263+
264+
#eval myCircle -- Shows the full structure
265+
#eval myCircle.area -- Calculates area
266+
#eval myCircle.circumference -- Calculates circumference
267+
#eval myCircle.containsPoint { x := 2.0, y := 2.0 } -- Tests point containment
268+
269+
/-
270+
-------------------------------------------------------------
271+
## Propositions as Types – A Glimpse into Proofs in Lean
272+
273+
In Lean, every proposition is just a type, and a proof is a value (or term) of that type.
274+
This is the essence of the propositions-as-types (or Curry–Howard) correspondence.
275+
In other words, proving a proposition amounts to constructing a term that inhabits the type
276+
representing that proposition.
277+
-------------------------------------------------------------
278+
-/
279+
280+
-- This function claims it returns a Nat
281+
def t1 : Nat := 0 -- putting a string here would be a type error
282+
283+
#check t1 -- Nat
284+
#eval t1 -- 0
285+
286+
def t2 : Nat := 0
287+
288+
def t3 (n : Nat) : Nat := n
289+
290+
-- def t4' n := n -- Thus doesn't work because Lean cannot infer a type
291+
292+
/-
293+
Compare with:
294+
Python:
295+
def foo(x):
296+
return x
297+
-/
298+
299+
-- But we can "hack" our way around this by making
300+
-- the arbitrary type of 'n' an argument of the method
301+
def t4 (T : Type) (n : T) : T := n
302+
303+
#eval t4 Nat 2
304+
305+
def t4' {T : Type} (n : T) : T := n -- curly brackets make T implicit
306+
307+
#eval t4' 2
308+
309+
-- We can prove that t3 and t4 applied to Nat return the same output!
310+
def t4_Nat_eq_t3 : t4 Nat = t3 := rfl
311+
312+
-- doesn't really matter if we use 'def' or 'theorem' here
313+
theorem t4_Nat_eq_t3' : t4 Nat = t3 := rfl
314+
315+
-- This does not work because not only the type is checked (Nat)
316+
-- but also the specific instance, which is not the same (0 != n)
317+
-- example : t4 Nat = t2 := rfl
318+
319+
-- A constructive proof of the type of the statement `P → P`
320+
def t5 (P : Prop) (p : P) : P := p
321+
322+
theorem t6 (P : Prop) (p : P) : P := by
323+
exact p -- same proof / method
324+
325+
326+
-- Blurring the lines between programming a method and writing a proof:
327+
-- How to proof P ∧ Q → P
328+
329+
-- Term mode proof
330+
theorem t7 (P Q : Prop) : P ∧ Q → P := fun ⟨p, _⟩ => p
331+
332+
theorem t7' (P Q : Prop) : (P ∧ Q → P : Prop) := fun ⟨p, _⟩ => p
333+
334+
-- Same proof in tactic mode
335+
theorem t7'' (P Q : Prop) : P ∧ Q → P := by
336+
intro ⟨p, _⟩
337+
exact p
338+
339+
-- They all have the same type and hence are proving the same theorem
340+
#check t7 -- ∀ (P Q : Prop), P ∧ Q → P
341+
#check t7' -- ∀ (P Q : Prop), P ∧ Q → P
342+
#check t7'' -- ∀ (P Q : Prop), P ∧ Q → P
343+
344+
-- sorry skips the proof but type checker is happy
345+
example (P Q : Prop) : P ∧ Q → P := by sorry
346+
347+
-- axioms don't require proofs!
348+
-- but this one is unnecessary, since it is inferred by our type system
349+
axiom this_is_our_first_axiom (P Q : Prop) : P ∧ Q → P

0 commit comments

Comments
 (0)