Functions & Definitions
Functions are the heart of Lean programming. Learn to define them with explicit types, type inference, named arguments, and default values.
Basic Function Definitions
Use the def keyword to define functions. The syntax is clean and expressive:
1-- A function with explicit types2def add (x : Nat) (y : Nat) : Nat := x + y34-- Using the function5#eval add 3 5 -- 867-- A function that takes a String8def greet (name : String) : String := "Hello, " ++ name ++ "!"910#eval greet "Lean" -- "Hello, Lean!"The pattern is: def name (params) : ReturnType := body. Each parameter has its own parentheses and type annotation.
Type Inference
Lean's type inference is powerful. You can often omit types and let the compiler figure them out:
1-- Type inferred for simple values2def x := 42 -- Inferred: Nat3def name := "Alice" -- Inferred: String45-- Type inferred for function parameters6def double x := x + x -- Inferred: Nat → Nat78-- But explicit types are clearer for functions9def double' (x : Nat) : Nat := x + xMultiple Parameters
Functions can take multiple parameters. Each parameter is in its own parentheses, or you can group parameters of the same type:
1-- Each parameter separate2def add1 (x : Nat) (y : Nat) : Nat := x + y34-- Grouped parameters of same type 5def add2 (x y : Nat) : Nat := x + y67-- Mixed types8def describe (name : String) (age : Nat) : String :=9 s!"{name} is {age} years old"1011#eval describe "Alice" 30 -- "Alice is 30 years old"Named Arguments
You can call functions with named arguments for clarity:
1def greet (name : String) (formal : Bool) : String :=2 if formal then s!"Good day, {name}." else s!"Hey {name}!"34-- Positional arguments5#eval greet "Alice" true -- "Good day, Alice."67-- Named arguments (order doesn't matter)8#eval greet (name := "Bob") (formal := false) -- "Hey Bob!"9#eval greet (formal := true) (name := "Carol") -- "Good day, Carol."Default Values
Parameters can have default values, making them optional:
1def greet (name : String) (formal : Bool := false) : String :=2 if formal then s!"Good day, {name}." else s!"Hey {name}!"34-- Using default5#eval greet "Alice" -- "Hey Alice!" (formal defaults to false)67-- Override default8#eval greet "Bob" true -- "Good day, Bob."910-- Named argument to override11#eval greet "Carol" (formal := true) -- "Good day, Carol.":= value are optional. This pattern is common for configuration options and flags.Anonymous Functions (Lambdas)
Create inline functions with fun (or the Unicode λ):
1-- Lambda syntax2#eval (fun x => x + 1) 5 -- 63#eval (fun x y => x * y) 3 4 -- 1245-- With type annotations6#eval (fun (x : Nat) => x * 2) 5 -- 1078-- Unicode lambda (optional but pretty)9#eval (λ x => x + 1) 5 -- 61011-- Lambdas are often used with higher-order functions12#eval [1, 2, 3].map (fun x => x * 2) -- [2, 4, 6]Implicit Arguments
Some arguments can be inferred from context. These are marked with curly braces:
1-- The type α is implicit - Lean infers it2def identity {α : Type} (x : α) : α := x34-- Lean infers α = Nat from the argument5#eval identity 42 -- 426#eval identity "hello" -- "hello" (α = String)78-- You can provide implicit args explicitly with @9#eval @identity Nat 42 -- 42Deep Dive: Why Implicit Arguments?
Implicit arguments reduce verbosity. Without them, you'd have to writeidentity Nat 42 every time, even though Lean can clearly see 42 is a Nat.
The @ prefix makes all arguments explicit, which is useful when type inference fails or you want to be specific.
Function Types
Functions have types expressed with arrows. Understanding these helps you read library documentation:
1-- Single argument function2#check (fun x => x + 1 : Nat → Nat)34-- Multiple arguments (curried)5#check (fun x y => x + y : Nat → Nat → Nat)6-- Read as: takes a Nat, returns a function Nat → Nat78-- With implicit arguments9#check @List.map10-- {α : Type} → {β : Type} → (α → β) → List α → List βPartial Application
Because multi-argument functions are curried, you can partially apply them:
1def add (x y : Nat) : Nat := x + y23-- Partially apply: fix the first argument4def add5 := add 5 -- add5 : Nat → Nat56#eval add5 3 -- 87#eval add5 10 -- 1589-- Useful with higher-order functions10#eval [1, 2, 3].map (add 10) -- [11, 12, 13]Where Clauses
For complex functions, use where to define helpers:
1def quadratic (a b c x : Float) : Float := 2 a * x^2 + b * x + c34-- Cleaner with where5def quadratic' (a b c x : Float) : Float := 6 term1 + term2 + term37where8 term1 := a * x^29 term2 := b * x10 term3 := c1112#eval quadratic' 1 2 1 3 -- 16.0