Best Practices
Writing idiomatic Lean 4 code requires more than just knowing syntax. Follow these guidelines to write code that is readable, maintainable, and aligned with the community.
Naming Conventions
Lean follows specific casing rules. Sticking to these makes your code look native.
- CamelCase for types, structures, classes, and modules.
Example:MyType,List.map,HashMap - camelCase for functions, definitions, local variables, and theorems.
Example:myFunction,calculateSum,isPrime - snake_case is rarely used in core Lean 4, though you might see it in legacy mathlib code.
Explicit vs Implicit Types
Lean's type inference is powerful, but readability is king.
Do Type:
- Top-level function signatures (always!)
- Structure fields
- Ambiguous literals (e.g.,
(5 : Float))
1-- Good: explicit types for top-level2def add (x : Nat) (y : Nat) : Nat := x + y34-- Bad: relying on inference for public API5def add x y := x + yDon't Type:
- Local variables where type is obvious
- Match arms where patterns imply types
Do Notation Usage
Use do notation for monadic code (IO, Option, etc.). Avoid raw bind (>>=) operators unless the pipeline is very short (1-2 steps).
1-- Good: readable, imperative style2def safeCompute (x : Nat) : Option Nat := do3 let y ← safeSubtract x 104 let z ← safeDivide y 25 return z67-- Avoid: confusing operator chains for complex logic8def hardToRead (x : Nat) : Option Nat :=9 safeSubtract x 10 >>= fun y => safeDivide y 2 >>= fun z => pure zTermination
Avoid partial unless absolutely necessary.partial functions cannot be used in proofs and can hide infinite loops. Try to use structural recursion or termination_by first.
Structure vs Class
| Use Structure | Use Class |
|---|---|
| Grouping data (Product types) | Defining behavior (Interfaces) |
| A Point with x/y coordinates | Standard equality check (BEq) |
| Users create values explicitly | Compiler finds instances automatically |
Pattern Matching
Prefer the dot syntax match x with or the function match shorthand when possible.
1-- Good: shorthand for simple cases2def isZero : Nat → Bool3 | 0 => true4 | _ => false56-- Good: dot notation for structures7def getX (p : Point) : Int :=8 match p with9 | { x, .. } => xUse the Standard Library
Before writing a helper function, check List, Array, and String namespaces. Functions like filterMap,find?, any, and all are often built-in.
[]. to see List functions.