1 History and development

Lean was created as a theorem prover and programming language aimed at combining expressive formal reasoning with practical software support. Its development has been shaped by two goals: making machine-checked mathematics more accessible and providing a usable environment for general-purpose programming. Over time, Lean evolved from an experimental proof assistant into a broad ecosystem used for research, teaching, and large-scale formalization.

1.1 Origins and goals

Lean originated in the work of logicians and computer scientists interested in dependent type theory as a foundation for mathematics and computation. The system was designed to support concise proofs, strong type safety, and a smooth transition between proving and programming. A central idea was that the same language should describe data, algorithms, and mathematical statements. This makes it possible to write executable code while also proving properties about that code inside the same framework.

1.2 Lean 3

Lean 3 established the system as a mature interactive theorem prover. It became known for its tactic framework, its support for mathematical formalization, and its growing library of reusable results. Users could construct proofs either by writing explicit terms or by guiding the system with tactics that break a goal into smaller pieces. Lean 3 also helped define many of the conventions later used in the ecosystem, especially in the development of large formal mathematics projects.

1.3 Lean 4

Lean 4 broadened the language into a more capable programming platform while preserving its proof assistant roots. It introduced a redesigned implementation with improved performance, a more powerful metaprogramming system, and a modernized syntax and elaboration process. Lean 4 is notable for making it easier to write both verified mathematics and ordinary software in the same environment. It also strengthened support for tooling, editor integration, and package-based development.

1.4 Community and ecosystem growth

Lean’s community expanded through online collaboration, shared libraries, tutorial material, and large formalization projects. The growth of mathlib, the main mathematical library, played a major role in attracting users and standardizing patterns of formal proof. As the ecosystem matured, Lean became a common choice for researchers who wanted a proof assistant with strong automation and a relatively approachable interface. Community practices such as code sharing, documentation, and reusable theorem libraries helped reduce duplication and encouraged collaborative formal work.

2 Core concepts

Lean is built around dependent type theory, in which types may depend on values and logical propositions are treated as types. This allows the system to represent both computation and proof in a single formal language. A theorem is expressed as a type, and proving it means constructing a term that inhabits that type. The result is a framework where correctness is checked by the type system itself.

2.1 Dependent type theory

Dependent type theory extends ordinary type systems by allowing types to refer to terms. In Lean, this makes it possible to express precise relationships between inputs and outputs, as well as rich logical statements. For example, a function returning a vector of a certain length can record that length in its type. This level of precision supports both safe programming and detailed formal reasoning.

2.2 Theorems and proofs

In Lean, a theorem is not separate from a programmatic object; it is a declaration whose type encodes the statement to be proved. A proof is a term that demonstrates the statement by satisfying the required type. This view aligns proof construction with type checking rather than with informal derivation alone. Once a proof term is accepted, the theorem is guaranteed by the kernel, the trusted core of the system.

2.3 Terms, types, and propositions

Lean distinguishes between terms, which are expressions, and types, which classify those expressions. Propositions are types whose inhabitants correspond to proofs. This correspondence is often described through the propositions-as-types principle. It allows logical connectives and quantifiers to be represented in a uniform way, making formal statements composable and suitable for mechanical verification.

2.4 Computation and reduction

Lean evaluates expressions by reduction, simplifying terms according to definitional rules. Computation is central to the system because many proof obligations can be discharged by evaluating expressions until their equality becomes evident. Reduction also supports definitional equality, a form of equivalence used by the type checker. This means that some transformations are accepted without explicit proof when they follow from the built-in computation rules.

2.5 Universe levels

Universe levels prevent certain logical inconsistencies by separating types into a hierarchy. Instead of allowing all types to live in a single category, Lean organizes them into levels such as Type 0, Type 1, and higher. This structure helps avoid paradoxes while preserving expressive power. Universe polymorphism allows definitions to work uniformly across different levels, which is especially useful in abstract mathematics.

3 Language features

Lean combines theorem-proving facilities with language features familiar from functional programming. It supports algebraic data types, higher-order functions, pattern matching, and type class-based abstraction. The language is designed so that proofs and programs share the same syntax and infrastructure, making it practical to move between formal reasoning and implementation.

3.1 Functional programming foundations

Lean is rooted in functional programming, with functions as first-class values and immutable data as a default. Expressions are typically built from function application, lambda abstraction, and recursive definitions. This style fits naturally with mathematical reasoning, since functions can be treated declaratively. It also supports concise code and a clear correspondence between program structure and proof structure.

3.2 Inductive types

Inductive types are one of Lean’s most important mechanisms for defining structured data. They include familiar forms such as natural numbers, lists, trees, and options, as well as custom logical and mathematical objects. Inductive definitions specify how values of a type are built from constructors. They also provide induction principles, which are essential for proving properties about recursively defined objects.

3.3 Pattern matching

Pattern matching lets users define functions and proofs by analyzing the shape of data. In Lean, a match expression can distinguish between constructors of an inductive type and bind their components for use in the result. This feature makes definitions readable and mirrors case-based mathematical reasoning. It is especially useful when defining recursive functions or proving statements by structural induction.

3.4 Type classes

Type classes provide a form of ad hoc polymorphism and automated inference. They let Lean infer operations and properties from context, such as equality, ordering, or algebraic structure. This mechanism is widely used in mathematics, where many theorems apply to any structure satisfying certain axioms. Type classes reduce boilerplate and make generic code and generic proofs easier to write.

3.5 Macros and metaprogramming

Lean includes metaprogramming facilities for extending the language and automating tasks. Macros can introduce new syntactic forms that expand into core language constructs. More advanced metaprogramming tools allow programs to inspect and transform expressions, build tactics, and generate code. This makes it possible to create domain-specific notation and proof automation tailored to particular areas of formalization.

4 Proof construction

Lean offers several ways to build proofs, from direct term writing to interactive tactic scripts. These approaches can be mixed within a single development, allowing users to choose the style best suited to the problem. The proof language is designed to be precise but flexible, with automation handling routine steps and human guidance reserved for more intricate arguments.

4.1 Term mode

In term mode, a proof is written as an explicit expression that directly inhabits the target proposition. This style is close to the underlying type-theoretic foundation and can be very compact for simple statements. It is often preferred when the proof is short, elegant, or closely tied to the structure of a definition. Term mode also makes the logical content of a proof especially transparent.

4.2 Tactic mode

Tactic mode lets users interactively decompose a goal into smaller subgoals. Each tactic performs a proof transformation, such as introducing hypotheses, applying a theorem, or simplifying an expression. This mode is popular because it resembles step-by-step mathematical reasoning. It is also useful for larger proofs, where explicit term construction would be unwieldy.

4.3 Structured proofs

Structured proofs organize arguments in a readable and maintainable way. They often combine declarations, intermediate lemmas, and tactical steps into a clear progression. Such proofs are valuable in larger developments because they separate conceptual steps from low-level details. A well-structured proof can be easier to review, reuse, and modify than a densely compressed script.

4.4 Automation tools

Lean provides automated procedures that handle many routine proof obligations. These tools can simplify expressions, apply known facts, and search for compatible transformations. Automation is especially important in large formalizations, where repeated low-level reasoning would otherwise become tedious. Users typically combine automation with manual steps to balance convenience and control.

4.4.1 Simplification

Simplification rewrites expressions using a collection of registered lemmas and definitional rules. It is commonly used to normalize expressions, eliminate trivial equalities, and reduce goals to a more manageable form. Because simplification can apply many rules automatically, it is one of the most frequently used proof tools in Lean.

4.4.2 Rewriting

Rewriting replaces an expression with an equal one using a proved equality. This technique is fundamental in formal proofs, where one often transforms a goal into a more convenient statement. Lean supports rewriting in both directions when appropriate, and it can be combined with simplification or other tactics. Rewriting is especially useful for equations, algebraic identities, and inductive arguments.

4.4.3 Decision procedures

Decision procedures solve certain classes of logical or algebraic goals automatically. Examples include propositional reasoning, arithmetic fragments, and equality checking for specific structures. These procedures are useful because they can settle routine subgoals with little user intervention. Their scope is limited, but within that scope they provide reliable and efficient assistance.

5 Mathematical library

Lean’s mathematical library provides a large body of formalized results and reusable definitions. It supplies foundational material, abstract algebra, analysis, and other areas needed for serious formal mathematics. The library is not merely a collection of theorems; it also establishes conventions, type class instances, and proof patterns that shape everyday Lean use.

5.1 Mathlib overview

Mathlib is the main community-driven mathematical library for Lean. It includes a broad range of definitions, lemmas, and higher-level theorems arranged to support large-scale formalization. The library is designed for reuse, so later developments can build on earlier ones with minimal duplication. Its scope and organization make it a central resource for users working in formal mathematics.

5.2 Algebraic structures

Mathlib includes extensive support for algebraic structures such as semigroups, monoids, groups, rings, fields, modules, and ordered variants. These structures are typically expressed through type classes, which allow the same theorem to apply across many contexts. This approach captures familiar abstract patterns from algebra while keeping proofs generic. It also makes it possible to transfer results between related settings with relatively little extra code.

5.3 Number theory

Lean supports formal number theory from elementary arithmetic to deeper results. The library contains definitions and theorems about divisibility, primes, modular arithmetic, and related concepts. Number-theoretic formalization often serves as a test case for both the expressiveness of the system and the quality of its automation. The area is especially useful for demonstrating how computational and logical reasoning interact.

5.4 Analysis

Analysis in Lean covers real numbers, sequences, continuity, differentiation, integration, and topology-related notions. Because analysis relies on precise epsilon-style arguments and carefully managed approximations, it benefits from the exactness of machine-checked proofs. Formal analysis developments are often larger and more technical than elementary algebra, which makes library organization and reuse especially important. Lean’s expressive type system helps encode analytic hypotheses accurately.

5.5 Category theory

Category theory is represented in Lean through formal definitions of categories, functors, natural transformations, and related constructions. Its abstract style fits well with dependent type theory, where objects and morphisms can be encoded with precision. Category-theoretic formalization is useful both in pure mathematics and as a unifying language for other areas of the library. It also showcases Lean’s ability to handle highly structured abstractions.

5.6 Combinatorics

Combinatorics in Lean includes finite structures, counting arguments, and properties of discrete objects. Formal combinatorial proofs often combine induction, algebraic manipulation, and finite reasoning. Because many combinatorial results are constructive, they align well with Lean’s computational character. The area benefits from reusable definitions for finite sets, lists, graphs, and related objects.

6 Programming in Lean

Lean can be used as a general-purpose functional programming language as well as a proof assistant. Programs are written with the same syntax and type system used for formal reasoning. This gives programmers strong static guarantees and makes it possible to verify properties of code within the same environment. The language therefore supports both executable applications and formally checked algorithms.

6.1 Executable definitions

Executable definitions are functions and values that can be evaluated by Lean’s runtime. Users can define ordinary computational behavior for data processing, symbolic manipulation, and algorithmic tasks. Because definitions are type-checked in the same system as proofs, programs benefit from strong guarantees about input and output shapes. This is particularly valuable when a program must satisfy a specification stated as a theorem.

6.2 Recursion and termination

Recursive definitions are common in Lean, but they must satisfy termination requirements. The system checks that recursion is well founded or structurally decreasing, ensuring that evaluation does not loop indefinitely. This constraint preserves logical consistency while still allowing expressive algorithms. When needed, users can provide measures or other evidence that a recursive process eventually ends.

6.3 Modules and namespaces

Modules and namespaces organize code into manageable units. Namespaces prevent naming conflicts and help group related definitions, theorems, and instances. Modules support separation of files and selective importing of dependencies. These mechanisms are important in large projects, where many contributors work on interconnected developments.

6.4 Input/output support

Lean includes facilities for interaction with the outside world, though these are more limited than in some mainstream programming languages. Input/output operations can read data, write messages, and interface with runtime services in controlled ways. Because Lean is also a proof environment, such effects are typically kept separate from purely logical content. This separation helps maintain the reliability of formal reasoning.

6.5 Interoperability with external code

Lean can interoperate with external code, especially through its runtime and integration layers. This makes it possible to connect formal developments with existing software or to use Lean within broader computational workflows. Interoperability is useful for tooling, data processing, and performance-sensitive components. It also supports adoption by users who need formal verification alongside practical programming.

7 Theorem proving workflow

A typical Lean development moves from definitions to conjectures, then to proofs and verification. Users often refine statements iteratively, test proof ideas, and consult the library for existing lemmas. The workflow benefits from rapid feedback, since the system reports type errors and proof failures immediately. As projects grow, organization and debugging become as important as the logical content itself.

7.1 Writing definitions

Definitions introduce new objects, functions, and abbreviations into the environment. Good definitions in Lean are usually chosen to match both the intended mathematics and the needs of later proofs. Clear naming and appropriate abstraction can make a large difference in usability. Because definitions are checked rigorously, they also serve as part of the formal specification.

7.2 Stating lemmas and theorems

A theorem statement in Lean must be precise enough for the type checker to interpret unambiguously. Users often begin by formulating a lemma that captures a useful intermediate result before proving a larger claim. Good statements tend to balance generality with manageability, since overly specific formulations can hinder reuse while overly broad ones may be difficult to prove. The statement itself is often refined during development.

7.3 Debugging proofs

Proof debugging involves interpreting error messages, inspecting goals, and identifying where a script no longer matches the intended argument. Because Lean checks each step mechanically, mistakes are usually localized. Users may test smaller subproofs, simplify expressions, or search the library for a supporting lemma. This iterative process is an ordinary part of formal proof development.

7.4 Extracting and checking code

Lean allows proofs and executable content to coexist, but the system carefully distinguishes between trusted logical checking and runnable code. Code can be evaluated to test behavior, while the kernel checks that formal claims are justified. In practical use, this makes it possible to verify a specification and also inspect the resulting implementation. The combination is particularly useful when correctness is as important as execution.

8 Syntax and tooling

Lean’s usability depends heavily on its syntax, editor support, and build infrastructure. The language is designed to be readable for both mathematicians and programmers, with notation that can be extended when necessary. Tooling plays a major role in making large formal developments manageable, since users rely on fast feedback and project-wide organization.

8.1 Lean source files

Lean source files contain declarations, proofs, imports, and local notation. They are the basic units of development and are typically arranged into a hierarchy of modules. File structure matters because imports determine what definitions are available and influence compilation order. Well-organized files help keep formal projects comprehensible as they scale.

8.2 Editors and language server support

Lean is commonly used with editor integrations that provide syntax highlighting, goal display, auto-completion, and inline error reporting. Language server support makes interactive proof development much smoother by showing the current state of the proof environment. This feedback helps users write and adjust proofs efficiently. It is one reason Lean is approachable despite its logical sophistication.

8.3 Build tools and package management

Build tools manage compilation, dependencies, and project configuration. Package management allows users to reuse external libraries and lock down versions for reproducible builds. These facilities are essential for collaborative formalization, where many files and dependencies must remain consistent. They also help maintain long-term stability in developing codebases.

8.4 Error messages and diagnostics

Lean provides diagnostics intended to explain type mismatches, unresolved goals, and other issues. While some messages can be technical, they often point directly to the construct that failed to elaborate or check. Learning to interpret these diagnostics is a key skill for effective use of the system. Good error reporting improves both productivity and trust in the formal environment.

9 Applications

Lean is used in areas where exact reasoning is valuable, especially mathematics and verified computation. Its combination of proof assistant features and programming support makes it useful in educational, research, and software contexts. The system’s applications are shaped by the need for machine-checked reliability and reusable formal structures.

9.1 Formalized mathematics

Formalized mathematics is one of Lean’s most prominent uses. Researchers and contributors encode definitions, lemmas, and full theorems in a form that can be checked automatically. This provides strong assurance that proofs are correct according to the underlying formal system. It also encourages careful statement design and reuse of previously established results.

9.2 Verified algorithms

Lean can be used to specify and verify algorithms, ensuring that implementations satisfy precise properties. This is useful for algorithms where correctness is more important than informal confidence, such as arithmetic routines, search procedures, or transformations on structured data. The same environment can host both the implementation and the proof of its behavior. This integration reduces the gap between specification and execution.

9.3 Educational use

Lean has become a useful tool for teaching logic, proofs, and functional programming. Its interactive environment helps students see how formal statements are built and checked. Because the system gives immediate feedback, learners can experiment with proof ideas and learn from errors. Educational materials often use Lean to bridge informal mathematics and rigorous formalization.

9.4 Research in type theory and logic

Lean serves as a platform for exploring dependent type theory, proof automation, and formal semantics. Researchers use it to test ideas about foundations, elaboration, metaprogramming, and theorem proving. The system’s flexibility makes it suitable for experimenting with new proof techniques and language features. It therefore functions not only as a tool for formalization but also as a research environment.

10 Comparison with other proof assistants

Lean is often compared with other proof assistants because it shares their goal of machine-checked reasoning while differing in design philosophy and user experience. Comparisons usually focus on expressiveness, automation, library maturity, programming support, and the balance between strictness and convenience. No single system is universally best; each has strengths suited to different communities and projects.

10.1 Lean versus Coq

Lean and Coq are both based on dependent type theory and support interactive theorem proving. Coq has a long history and a very established ecosystem, while Lean is often praised for its automation, modern tooling, and integrated programming features. Lean’s tactics and library organization are frequently considered approachable by new users, though Coq also offers deep proof-engineering experience. The choice between them often depends on project history and community preference.

10.2 Lean versus Isabelle

Isabelle emphasizes a different style of proof development, with strong support for higher-level proof scripts and a flexible logical framework. Lean is more tightly centered on dependent type theory and a unified language for proofs and programs. Isabelle users may appreciate its mature automation and structured proof methods, while Lean users often value its direct term-based foundation and metaprogramming capabilities. The two systems represent distinct traditions in formal methods.

10.3 Lean versus Agda

Lean and Agda both combine dependent types with programming, but they serve somewhat different audiences. Agda is especially closely associated with type-theoretic programming and explicit proof terms, while Lean balances proof assistant functionality with a substantial theorem-proving ecosystem. Lean’s automation and mathlib are often highlighted as advantages for large formal mathematics projects. Agda, by contrast, is often valued for its direct style and expressive type-driven programming.

10.4 Strengths and limitations

Lean’s strengths include a powerful type system, effective automation, a growing mathematical library, and strong support for both proof and programming tasks. Its unified design makes it suitable for diverse formal developments. Limitations include the learning curve associated with dependent types, the need to master proof tactics and library conventions, and the fact that very large formal projects require careful engineering. As with any proof assistant, productivity often depends on how well the system matches the user’s goals and background.