Modules & Imports
Organize code into modules for reusability and maintainability. Control what's exported and how it's imported.
File = Module
Each .lean file is a module. The file path determines the module name:
text
1MyProject/2├── MyProject.lean -- Module: MyProject3├── MyProject/4│ ├── Utils.lean -- Module: MyProject.Utils5│ ├── Utils/6│ │ └── String.lean -- Module: MyProject.Utils.String7│ └── Core.lean -- Module: MyProject.CoreImporting Modules
lean
1-- Import entire module2import MyProject.Utils34-- Import brings all public definitions into scope5-- You can now use: Utils.helper, Utils.Config, etc.67-- Import multiple modules8import MyProject.Utils9import MyProject.Coreℹ
Imports are transitive: if A imports B and B imports C, then A has access to C's public definitions.
Namespaces
Group related definitions under a name:
lean
1namespace Math23def square (x : Nat) : Nat := x * x4def cube (x : Nat) : Nat := x * x * x56end Math78-- Use with qualified name9#eval Math.square 5 -- 2510#eval Math.cube 3 -- 27Opening Namespaces
lean
1-- Open brings names into scope2open Math34#eval square 5 -- No Math. prefix needed56-- Open temporarily in expression7#eval (open Math in square 5)89-- Open specific names only10open Math (square)11#eval square 5 -- Works12-- #eval cube 3 -- Error: not in scopeSections
Sections scope variables and attributes:
lean
1section VectorOps2 variable (n : Nat) -- Available in this section3 4 def zeros : List Nat := List.replicate n 05 def ones : List Nat := List.replicate n 16 7end VectorOps89-- n is no longer in scope here10#eval zeros 5 -- [0, 0, 0, 0, 0]Key Takeaway
Namespaces organize names. Sections scope variables andattributes. Often used together for clean module design.
Export and Visibility
Control what's visible outside your module:
lean
1-- Private: only visible in this file2private def helper (x : Nat) : Nat := x + 134-- Protected: visible with namespace prefix5protected def Config.default : Config := ⟨0, ""⟩67-- Public (default): visible everywhere8def publicFunc : Nat := 42Re-exporting
lean
1-- In MyProject/Prelude.lean2import MyProject.Utils3import MyProject.Core45-- Re-export so users only need one import6export MyProject.Utils (helper Config)7export MyProject.Core (main)lean
1-- Users just import Prelude2import MyProject.Prelude34-- Gets helper, Config, and mainModule Initialization
Code at the top level runs when the module loads:
lean
1-- This runs when module is imported2#check "Module loaded"34-- Use initialize for side effects5initialize : IO Unit := do6 IO.println "Initializing MyModule..."78-- Register attributes, extensions, etc.9initialize myExtension : EnvExtension Nat ← 10 registerEnvExtension (pure 0)Mutual Dependencies
Lean doesn't allow circular imports. Structure your code to avoid them:
lean
1-- Bad: A imports B, B imports A2-- This won't compile34-- Good: extract shared code to a third module5-- Common.lean - shared definitions6-- A.lean - imports Common7-- B.lean - imports CommonDeep Dive: Prelude and Init
Every Lean file implicitly imports the Prelude, which provides basic types and functions:
lean
1-- These are available without import:2-- Nat, Int, Bool, String, List, Option, etc.34-- To write a file without Prelude:5prelude -- Must be first lineThe prelude directive is used when defining the Prelude itself or for specialized low-level code.
Common Patterns
Barrel File
lean
1-- MyProject.lean (root module)2-- Re-exports everything for easy importing34import MyProject.Types5import MyProject.Utils6import MyProject.Core78export MyProject.Types9export MyProject.Utils 10export MyProject.CoreInternal Module
lean
1-- MyProject/Internal.lean2-- Implementation details not for public use34private def internalHelper := ...5private structure InternalState := ...67-- Only export what's needed by other internal modulesFeature Modules
lean
1-- Each feature in its own namespace2-- MyProject/Feature/Auth.lean3namespace MyProject.Feature.Auth4 def login := ...5 def logout := ...6end MyProject.Feature.Auth78-- MyProject/Feature/Data.lean 9namespace MyProject.Feature.Data10 def load := ...11 def save := ...12end MyProject.Feature.DataBest Practices
- One concept per module: Keep modules focused
- Match file and namespace:
MyProject/Utils.leandefinesMyProject.Utils - Use private for internals: Don't expose implementation details
- Provide a root module: Re-export the public API
- Avoid deep nesting: 3-4 levels maximum