Lean 4’s surface syntax is expressive and whitespace-sensitive. Every source file is a module, declarations are organized into namespaces, and the universe system lets you write polymorphic definitions that work at any type level. This page introduces the structural building blocks of every Lean 4 file — the syntax you will encounter before any logic or proofs.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/leanprover/lean4/llms.txt
Use this file to discover all available pages before exploring further.
Source Files and Modules
Every.lean file is a module. The module name corresponds to the file path relative to the package root (with / replaced by . and the .lean extension removed). A file at MyPkg/Data/List.lean defines the module MyPkg.Data.List.
Import Statements
import statements must appear at the very top of a file (before any declarations). They bring another module’s public declarations into scope:
Init) is imported automatically by default. To suppress this and start from scratch, use the prelude keyword:
In ordinary Lean files (those without an explicit
module keyword), import statements are re-exported by default, so transitive imports are visible. In files that begin with the module keyword, only public import statements are re-exported; bare import statements are private to that module.Comments
Declarations
The top-level commands that add definitions to the environment:Whitespace Sensitivity
Lean 4 uses indentation to delimit blocks. Ado block, match arms, where clauses, and tactic blocks all use indentation rather than explicit delimiters (though curly braces with semicolons are also accepted):
Namespaces
Namespaces group related declarations and avoid name collisions.Opening Namespaces
open brings names from a namespace into scope without full qualification:
Sections and variable
Sections let you declare shared parameters once instead of repeating them on every definition:
variable command declares parameters that are automatically included as implicit or instance arguments on any definition in the current scope that mentions them.
Universe Polymorphism
Lean’s type hierarchy is:Sort 0 = Prop, Sort 1 = Type = Type 0, Sort 2 = Type 1, …
You can write universe-polymorphic definitions using universe variables:
Type* (or Type _) lets Lean infer the universe level:
Attributes
Attributes annotate declarations with metadata that affects elaboration, simplification, or code generation:Query Commands
These commands query the current environment and are invaluable during development:#check
Displays the type of an expression:
#eval
Evaluates an expression and prints the result:
#print
Prints the full definition of a declaration, including its type and body:
set_option
Lean’s behaviour can be tuned with set_option:
Summary of Key Syntax Elements
| Syntax | Purpose |
|---|---|
import Foo.Bar | Import a module |
namespace Foo … end Foo | Open a namespace block |
open Foo | Bring namespace into scope |
section … end | Delimit a variable scope |
variable (x : α) | Declare a shared parameter |
universe u | Declare a universe variable |
@[attr] | Apply an attribute |
#check e | Print the type of e |
#eval e | Evaluate and print e |
#print name | Print a declaration’s definition |
-- comment | Single-line comment |
/- … -/ | Block comment |
/-- … -/ | Doc-string comment |