| # 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` | |
| ``` |