Basics

Add Numbers

i
#eval 2 + 2
i
4

Defining Values

i
def sum : Int := 2 + 2
#eval sum
i
4

Strict Typing

i
def nope : Int := 2 + "two"
i
basics/strict_values.lean:1:18: error(lean.synthInstanceFailed): failed to synthesize instance of type class
  HAdd Nat String Int

Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.

Guards

i
def language: String := "Lean"
#guard language.length == 4

Formatting Strings

i
def name : String := "Jesse"
def count : Nat := 3
#eval s!"{name} has done {count} exercises"
i
"Jesse has done 3 exercises"

Lists

i
def names : List String := ["first", "second", "third"]
#eval s!"names has {names.length} elements"
#eval s!"element 0 of names is '{names[0]}'"
i
"names has 3 elements"
"element 0 of names is 'first'"

Defining Simple Functions

i
def alwaysTheSame : Float :=
  42.0

#eval alwaysTheSame
i
42.000000
i
def double (value : Float) : Float :=
  2 * value

#eval double 3.0
i
6.000000
i
def multiply (left : Float) (right : Float) : Float :=
  left * right

#eval multiply 3 4
i
12.000000

What's Actually Happening

i
def multiply (left : Int) (right : Int) : Int :=
  left * right

def temp := multiply 3

#eval temp 4
i
12

Conditionals

i
def classify (n : Int) : String :=
  if n > 0 then "positive"
  else if n < 0 then "negative"
  else "zero"

#eval classify 5
#eval classify (-3)
#eval classify 0
i
"positive"
"negative"
"zero"
i
def classify (n : Int) : String :=
  if n > 0 then "positive"
i
basics/conditional_incomplete.lean:3:0: error: unexpected end of input; expected 'else'
i
def classify (n : Int) : String :=
  if n > 0 then "positive"
  else 3
i
basics/conditional_inconsistent.lean:3:7: error(lean.synthInstanceFailed): failed to synthesize instance of type class
  OfNat String 3
numerals are polymorphic in Lean, but the numeral `3` cannot be used in a context where the expected type is
  String
due to the absence of the instance above

Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.

Another Way to Do It

i
#eval match 3 with
 | 0 => "zero"
 | _ => "nonzero"
i
"nonzero"

Recursion

i
def sumList (xs : List Int) : Int :=
  match xs with
  | []      => 0
  | x :: xs => x + sumList xs

#eval sumList [1, 3, 5]
i
9

Map, Filter, and Fold

i
def numbers : List Int := [1, 3, 5, 7]

-- map transforms every element
#eval numbers.map (fun val => val * 2)

-- filter keeps only matching elements
#eval numbers.filter (fun val => val >= 5)

-- foldl accumulates from the left
#eval numbers.foldl (fun left right => left + right) 0
i
[2, 6, 10, 14]
[5, 7]
16

Local Definitions

i
def circleArea (radius : Float) : Float :=
  let pi := 3.14159
  let rSquared := radius * radius
  pi * rSquared

#eval circleArea 2.0
i
12.566360

Tuples

i
def pair : Int × Int := (10, 20)

#eval pair.1
#eval pair.2

def swap (p : Int × Int) : Int × Int :=
  let (x, y) := p
  (y, x)

#eval swap pair
i
10
20
(20, 10)

Missing Values

i
def firstElement (xs : List Int) : Option Int :=
  match xs with
  | []     => Option.none
  | x :: _ => Option.some x

#eval firstElement [10, 20, 30]
#eval firstElement []

def firstOrZero (xs : List Int) : Int :=
  match firstElement xs with
  | Option.none   => 0
  | Option.some x => x

#eval firstOrZero [10, 20, 30]
#eval firstOrZero []
i
some 10
none
10
0

Structures

i
structure Point where
  x : Float
  y : Float

def origin : Point := { x := 0.0, y := 0.0 }
def here : Point := { x := 3.0, y := 4.0 }

#eval origin.x
#eval here.y

def distance (p : Point) : Float :=
  Float.sqrt (p.x * p.x + p.y * p.y)

#eval distance here
i
0.000000
4.000000
5.000000

Checking and Updating

i
#check 123

structure Point where
  x : Float
  y : Float

def origin : Point := { x := 0.0, y := 0.0 }
#check origin

def elsewhere : Point := {origin with x := 1.0 }
#eval elsewhere
i
123 : Nat
origin : Point
{ x := 1.000000, y := 0.000000 }

Type Inference

i
-- Lean can infer the type from the value
def greeting := "hello"
def count := 42

#eval greeting
#eval count

-- Lean can infer the return type of a function
def double (val : Int) := val * 2

#eval double 7
i
"hello"
42
14

Idiomatic Lean

i
def numbers : List Int := [1, 3, 5, 7]

-- use · shorthand for simple lambdas
#eval numbers.map (· * 2)

-- use mathematical symbols
#eval numbers.filter (·  5)

-- each · matches a parameter in order
#eval numbers.foldl (· + ·) 0
i
[2, 6, 10, 14]
[5, 7]
16

Exercises

Fix: Missing Else Branch

i
-- This function should return "positive", "negative", or "zero"
-- but it has a bug. Fix it.
def describe (n : Int) : String :=
  if n > 0 then
    "positive"
  else if n < 0 then
    "negative"
hint
  • The describe function doesn't handle the case when n is zero
  • The compiler will reject this: add the missing else branch

Fix: Non-Exhaustive Match

i
-- This function should return the first element of a list
-- or 0 if the list is empty. Fix the bug.
def firstOrZero (xs : List Int) : Int :=
  match xs with
  | x :: _ => x
hint
  • firstOrZero only handles the case where the list has at least one element
  • Add a branch for the empty list that returns 0

Fix: Unhandled Option

i
-- This function should extract a value from an Option
-- or return "missing" for Option.none. Fix the bug.
def getValue (opt : Option String) : String :=
  match opt with
  | Option.some s => s
hint
  • getValue only handles Option.some but not Option.none
  • Add a branch that returns "missing" when the option is none

Fix: Wrong Base Case

i
-- This function should return the product of all numbers in a list
-- but it always returns 0. Fix the bug.
def product (xs : List Int) : Int :=
  match xs with
  | [] => 0
  | x :: rest => x * product rest
hint
  • Multiplying by zero gives zero, so the base case [] => 0 breaks everything
  • What should the product of an empty list be? (Hint: think about 1 * 2 * 3)

Fix: Wrong Return Type

i
-- This function should return the sum of two integers
-- but the type annotation is wrong. Fix the bug.
def add (x : Int) (y : Int) : String :=
  x + y
hint
  • The function body returns an Int but the type annotation says String
  • Fix the type annotation to match what the body actually returns

Write: Double a Number

i
-- Write a function 'double' that multiplies a Float by 2.
def double (x : Float) : Float :=
  0.0

#guard double 3.0 == 6.0
#guard double (-1.5) == -3.0
hint
  • Replace 0.0 with an expression that doubles x
  • The #guard statements will tell you if your solution is correct

Write: Make a Greeting

i
-- Write a function 'greet' that returns s!"Hello, {name}!".
def greet (name : String) : String :=
  ""

#guard greet "World" == "Hello, World!"
#guard greet "Lean" == "Hello, Lean!"
hint
  • Replace "" with a string that includes the person's name
  • Use s!"…" interpolation like the examples earlier in the lesson

Write: List Length

i
-- Write a function 'myLength' that returns the length of a List Int
-- using recursion. Do not use List.length.
def myLength (xs : List Int) : Nat :=
  0

#guard myLength [] == 0
#guard myLength [1, 2, 3] == 3
#guard myLength [5] == 1
hint
  • Write a recursive function that counts the elements in a list
  • Do not use List.length — implement it yourself with match and ::

Write: Swap a Tuple

i
-- Write a function 'swap' that takes (Int, String)
-- and returns (String, Int) with the elements exchanged.
def swap (p : Int × String) : String × Int :=
  ("", 0)

#guard swap (42, "hello") == ("hello", 42)
#guard swap (7, "seven") == ("seven", 7)
hint
  • Replace ("", 0) with the elements of p in reverse order
  • Use .1 and .2 to access the fields of the tuple

Write: Update a Structure

i
structure Point where
  x : Float
  y : Float

-- Write a function 'moveRight' that takes a Point
-- and returns a new Point with x increased by 1.0.
def moveRight (p : Point) : Point :=
  p

#guard moveRight { x := 3.0, y := 0.0 } == { x := 4.0, y := 0.0 }
#guard moveRight { x := 0.0, y := 5.0 } == { x := 1.0, y := 5.0 }
hint
  • Replace p with a new Point that has x incremented by 1.0
  • Use the { old with field := newVal } syntax shown in the structures section