I deleted internal links that seemed to have the character of "TODO". I think that the residual TODO is of little value, given that we plan a big revamp and revision soon anyway, but I could do it some other way as well.
200 lines
6.4 KiB
Markdown
200 lines
6.4 KiB
Markdown
# Tour of Lean
|
|
|
|
The best way to learn about Lean is to read and write Lean code.
|
|
This article will act as a tour through some of the key features of the Lean
|
|
language and give you some code snippets that you can execute on your machine.
|
|
To learn about setting up a development environment, check out [Setting Up Lean](./setup.md).
|
|
|
|
There are two primary concepts in Lean: functions and types.
|
|
This tour will emphasize features of the language which fall into
|
|
these two concepts.
|
|
|
|
# Functions and Namespaces
|
|
|
|
The most fundamental pieces of any Lean program are functions organized into namespaces.
|
|
[Functions](./functions.md) perform work on inputs to produce outputs,
|
|
and they are organized under [namespaces](./namespaces.md),
|
|
which are the primary way you group things in Lean.
|
|
They are defined using the `def` command,
|
|
which give the function a name and define its arguments.
|
|
|
|
```lean
|
|
namespace BasicFunctions
|
|
|
|
-- The `#eval` command evaluates an expression on the fly and prints the result.
|
|
#eval 2+2
|
|
|
|
-- You use 'def' to define a function. This one accepts a natural number
|
|
-- and returns a natural number.
|
|
-- Parentheses are optional for function arguments, except for when
|
|
-- you use an explicit type annotation.
|
|
-- Lean can often infer the type of the function's arguments.
|
|
def sampleFunction1 x := x*x + 3
|
|
|
|
-- Apply the function, naming the function return result using 'def'.
|
|
-- The variable type is inferred from the function return type.
|
|
def result1 := sampleFunction1 4573
|
|
|
|
-- This line uses an interpolated string to print the result. Expressions inside
|
|
-- braces `{}` are converted into strings using the polymorphic method `toString`
|
|
#eval println! "The result of squaring the integer 4573 and adding 3 is {result1}"
|
|
|
|
-- When needed, annotate the type of a parameter name using '(argument : type)'.
|
|
def sampleFunction2 (x : Nat) := 2*x*x - x + 3
|
|
|
|
def result2 := sampleFunction2 (7 + 4)
|
|
|
|
#eval println! "The result of applying the 2nd sample function to (7 + 4) is {result2}"
|
|
|
|
-- Conditionals use if/then/else
|
|
def sampleFunction3 (x : Int) :=
|
|
if x > 100 then
|
|
2*x*x - x + 3
|
|
else
|
|
2*x*x + x - 37
|
|
|
|
#eval println! "The result of applying sampleFunction3 to 2 is {sampleFunction3 2}"
|
|
|
|
end BasicFunctions
|
|
```
|
|
|
|
```lean
|
|
-- Lean has first-class functions.
|
|
-- `twice` takes two arguments `f` and `a` where
|
|
-- `f` is a function from natural numbers to natural numbers, and
|
|
-- `a` is a natural number.
|
|
def twice (f : Nat → Nat) (a : Nat) :=
|
|
f (f a)
|
|
|
|
-- `fun` is used to declare anonymous functions
|
|
#eval twice (fun x => x + 2) 10
|
|
|
|
-- You can prove theorems about your functions.
|
|
-- The following theorem states that for any natural number `a`,
|
|
-- adding 2 twice produces a value equal to `a + 4`.
|
|
theorem twiceAdd2 (a : Nat) : twice (fun x => x + 2) a = a + 4 :=
|
|
-- The proof is by reflexivity. Lean "symbolically" reduces both sides of the equality
|
|
-- until they are identical.
|
|
rfl
|
|
|
|
-- `(· + 2)` is syntax sugar for `(fun x => x + 2)`. The parentheses + `·` notation
|
|
-- is useful for defining simple anonymous functions.
|
|
#eval twice (· + 2) 10
|
|
|
|
-- Enumerated types are a special case of inductive types in Lean,
|
|
-- which we will learn about later.
|
|
-- The following command creates a new type `Weekday`.
|
|
inductive Weekday where
|
|
| sunday : Weekday
|
|
| monday : Weekday
|
|
| tuesday : Weekday
|
|
| wednesday : Weekday
|
|
| thursday : Weekday
|
|
| friday : Weekday
|
|
| saturday : Weekday
|
|
|
|
-- `Weekday` has 7 constructors/elements.
|
|
-- The constructors live in the `Weekday` namespace.
|
|
-- Think of `sunday`, `monday`, …, `saturday` as being distinct elements of `Weekday`,
|
|
-- with no other distinguishing properties.
|
|
-- The command `#check` prints the type of a term in Lean.
|
|
#check Weekday.sunday
|
|
#check Weekday.monday
|
|
|
|
-- The `open` command opens a namespace, making all declarations in it accessible without
|
|
-- qualification.
|
|
open Weekday
|
|
#check sunday
|
|
#check tuesday
|
|
|
|
-- You can define functions by pattern matching.
|
|
-- The following function converts a `Weekday` into a natural number.
|
|
def natOfWeekday (d : Weekday) : Nat :=
|
|
match d with
|
|
| sunday => 1
|
|
| monday => 2
|
|
| tuesday => 3
|
|
| wednesday => 4
|
|
| thursday => 5
|
|
| friday => 6
|
|
| saturday => 7
|
|
|
|
#eval natOfWeekday tuesday
|
|
|
|
def isMonday : Weekday → Bool :=
|
|
-- `fun` + `match` is a common idiom.
|
|
-- The following expression is syntax sugar for
|
|
-- `fun d => match d with | monday => true | _ => false`.
|
|
fun
|
|
| monday => true
|
|
| _ => false
|
|
|
|
#eval isMonday monday
|
|
#eval isMonday sunday
|
|
|
|
-- Lean has support for type classes and polymorphic methods.
|
|
-- The `toString` method converts a value into a `String`.
|
|
#eval toString 10
|
|
#eval toString (10, 20)
|
|
|
|
-- The method `toString` converts values of any type that implements
|
|
-- the class `ToString`.
|
|
-- You can implement instances of `ToString` for your own types.
|
|
instance : ToString Weekday where
|
|
toString (d : Weekday) : String :=
|
|
match d with
|
|
| sunday => "Sunday"
|
|
| monday => "Monday"
|
|
| tuesday => "Tuesday"
|
|
| wednesday => "Wednesday"
|
|
| thursday => "Thursday"
|
|
| friday => "Friday"
|
|
| saturday => "Saturday"
|
|
|
|
#eval toString (sunday, 10)
|
|
|
|
def Weekday.next (d : Weekday) : Weekday :=
|
|
match d with
|
|
| sunday => monday
|
|
| monday => tuesday
|
|
| tuesday => wednesday
|
|
| wednesday => thursday
|
|
| thursday => friday
|
|
| friday => saturday
|
|
| saturday => sunday
|
|
|
|
#eval Weekday.next Weekday.wednesday
|
|
-- Since the `Weekday` namespace has already been opened, you can also write
|
|
#eval next wednesday
|
|
|
|
-- Matching on a parameter like in the previous definition
|
|
-- is so common that Lean provides syntax sugar for it. The following
|
|
-- function uses it.
|
|
def Weekday.previous : Weekday -> Weekday
|
|
| sunday => saturday
|
|
| monday => sunday
|
|
| tuesday => monday
|
|
| wednesday => tuesday
|
|
| thursday => wednesday
|
|
| friday => thursday
|
|
| saturday => friday
|
|
|
|
#eval next (previous wednesday)
|
|
|
|
-- We can prove that for any `Weekday` `d`, `next (previous d) = d`
|
|
theorem Weekday.nextOfPrevious (d : Weekday) : next (previous d) = d :=
|
|
match d with
|
|
| sunday => rfl
|
|
| monday => rfl
|
|
| tuesday => rfl
|
|
| wednesday => rfl
|
|
| thursday => rfl
|
|
| friday => rfl
|
|
| saturday => rfl
|
|
|
|
-- You can automate definitions such as `Weekday.nextOfPrevious`
|
|
-- using metaprogramming (or "tactics").
|
|
theorem Weekday.nextOfPrevious' (d : Weekday) : next (previous d) = d := by
|
|
cases d -- A proof by case distinction
|
|
all_goals rfl -- Each case is solved using `rfl`
|
|
```
|