Lean occupies a unique place in today’s landscape of programming languages. It is not solely a language for building software, nor solely a language for formal mathematics. Instead, it stands at the intersection of both fields, merging the precision of mathematical logic with the expressive power of functional programming. To approach Lean for the first time is to step into a world where computation and proof are treated with equal respect, and where reasoning itself becomes something that can be executed, verified, and refined with the same rigor we apply to software. As you begin this course of one hundred articles, the aim is to guide you not just through the mechanics of Lean, but through the intellectual shift it invites. Lean does not simply offer new syntax or abstractions; it encourages a new way of seeing problems, a way rooted in clarity, structure, and uncompromising correctness.
Lean was conceived as a proof assistant, but calling it that alone undersells its depth. It is a language designed to express mathematical statements with exactness, to allow computers to verify the validity of those statements, and to empower humans to explore large mathematical spaces with a tool that faithfully enforces logical rules. But Lean is more than a tool for mathematics; it is also a functional programming language in its own right, built around the elegant foundations of dependent type theory. This makes it not only a medium for verifying proofs but also a way to write programs whose correctness is woven into their structure. In Lean, a program can be accompanied by guarantees that go far beyond type safety. It can encode precise logical conditions, invariants, and properties, and Lean will insist that these conditions are satisfied before a program is accepted.
This dual identity—part proof environment, part programming language—makes Lean intellectually fascinating. It offers an opportunity to understand software not just as a process of instructing machines, but as an act of constructing formally verified artifacts. This perspective has deep implications for how we reason, design, and build systems. For learners coming from traditional programming backgrounds, Lean can feel demanding at first. Its foundations lie in concepts such as propositions-as-types, dependent functions, and inductive reasoning. But these ideas, once absorbed, illuminate a powerful and unified framework for expressing logic and computation in the same breath. Many who work with Lean for a time describe the experience as transformative; it changes the way they think about correctness and invites a deeper appreciation of the connections between mathematical reasoning and software development.
What draws many learners to Lean is the ambition behind the project. It is part of a broader movement toward mechanizing mathematics and constructing libraries of formalized proofs that future generations can build upon. In ordinary mathematical practice, even the most careful proofs rely on conventions, omissions, and shared intuition. Lean, by contrast, demands that every assertion be justified. It does not accept hand-waving, nor does it allow ambiguity. The result is a system where once something is proven, it is proven in a way that is checkable by an unambiguous, machine-driven process. This ambition has already borne fruit in projects like mathlib, the rapidly growing library of formalized mathematics developed by a community of researchers, educators, and hobbyists. Through Lean, thousands of results spanning algebra, topology, number theory, and analysis are being encoded in a way that ensures their correctness indefinitely.
Yet Lean is not only about high-level mathematics. At a more immediate level, it invites you to slow down and think carefully about the structure of definitions, the flow of logic, and the meaning behind each statement. Even simple functions or theorems become exercises in precision. This carefulness encourages a discipline that many developers find refreshing. In traditional software engineering, correctness is often treated as an afterthought, something to be validated through testing, inspection, or good luck. In Lean, correctness is built into the process of writing the program or proof itself. Instead of discovering errors after the fact, you rule them out during construction. This shift offers a compelling glimpse of how future software systems might be built: through languages that blend expressive programming with formal reasoning from the beginning.
It is important, however, not to mistake Lean’s rigor for rigidity. The language offers great expressive freedom, and its design reflects a balance between mathematical precision and human usability. For instance, Lean’s system of type inference reduces the need for constant annotation, allowing users to write code or proofs that flow naturally. Its tactic framework provides a way to automate or assist in constructing proofs, making it possible to build complex arguments step by step or rely on automated strategies when appropriate. These features create an environment where both novices and experts can work effectively, gradually moving from guided interactions to more direct and sophisticated formulations.
One of the most striking aspects of Lean is the transparency it brings to reasoning. Concepts that are often implicit in mathematics—such as the structure of assumptions, the hierarchy of types, or the branching of cases—become explicit. This explicitness can feel unusual at first, especially if you come from a background where such details are left to intuition. But Lean’s insistence on clarity ultimately deepens understanding. When the system requires you to articulate every case in a proof or confirm that your definitions are well-formed, it forces you to confront the subtleties that are otherwise easy to overlook. Many learners find that working with Lean helps them uncover misunderstandings they didn't realize they had and encourages habits of thought that make them more precise thinkers.
Lean’s foundations in dependent type theory also open a window into some of the most profound ideas in the theory of programming languages. In dependent type theory, types can depend on values, and propositions are treated as types, while proofs are treated as programs that inhabit those types. This correspondence—the so-called Curry–Howard correspondence—reveals a deep unity between logic and computation. Lean brings this unity to life, allowing you to see propositions as entities you can manipulate, pass to functions, or transform. Learning Lean therefore offers more than practical skills; it provides a conceptual framework that enriches your understanding of what programming languages can be.
Although Lean’s core audience often includes mathematicians, the language is gaining increasing attention from software developers, researchers in formal methods, and practitioners interested in high-assurance systems. For domains where correctness is paramount—cryptography, distributed systems, verified compilers, and safety-critical applications—Lean demonstrates how programs can be developed with strong guarantees that extend far beyond what traditional type systems can provide. In these settings, the ability to encode precise logical conditions and have them mechanically verified is invaluable. As systems grow more complex and software becomes increasingly interwoven with society’s infrastructure, the demand for such guarantees is likely to grow.
What makes Lean especially compelling for learners is how naturally it reveals the structure of arguments. A proof in Lean is not a string of symbolic manipulations but a construction built piece by piece. Each lemma becomes a building block, each definition an element in a greater architecture. When you build a proof, you are not performing arcane rituals; you are assembling a logical structure that the computer can inspect in detail. This approach, while methodical, can become deeply satisfying as you see intricate results emerge from carefully arranged components.
Throughout this course, the intention is to illuminate both the practical and philosophical dimensions of Lean. You will explore how to define types, write functions, and construct proofs. You will see how dependent types allow you to encode rich structures with remarkable precision. You will learn how Lean manages universes to avoid paradoxes, how its tactic framework assists in automating proofs, and how its core logic provides a foundation for building vast libraries of formally checked mathematics. But beyond these technical aspects, you will also encounter the habits of thought that Lean cultivates—the patience to construct arguments carefully, the discipline to articulate every assumption, and the clarity that comes from reducing ambiguity.
As you progress, Lean will likely challenge you in ways that familiar programming languages do not. It may require more attention to detail, more reflection, and more willingness to engage with abstraction. But it will also reward you with insights that extend far beyond the surface of programming. Many learners describe Lean as reshaping their intellectual landscape. They begin to see the structure behind arguments more clearly, understand the flow of logic more deeply, and appreciate the relationship between programs and proofs more strongly. Even those who use Lean only occasionally often find that it influences how they design and reason about systems in other languages.
Perhaps the most profound aspect of Lean is the sense of trust it creates. When a theorem is proven in Lean, you can rely on it with absolute certainty. When a program typechecks, you know that it satisfies the properties encoded in its types. This trust is not based on intuition or experience but on formal verification by a system built on firm logical foundations. In a world where software bugs cause failures ranging from minor inconveniences to catastrophic losses, the ability to build systems with such confidence is not merely academically interesting; it is practically transformative.
As we begin this journey, it is helpful to view Lean not simply as a tool but as an intellectual companion—one that encourages precision, rewards patience, and clarifies thought. Whether your interest lies in pure mathematics, theoretical computer science, or the craft of programming itself, Lean has something to offer. It presents a language where ideas are expressed with clarity, where correctness is woven into every construction, and where the boundary between mathematics and computation becomes wonderfully thin.
The hundred articles that follow will unfold this world step by step. They will introduce you to Lean’s foundations, its methods, its conventions, and its distinctive way of thinking. Along the way, you will encounter challenges that ask you to reason carefully and opportunities that invite you to see programming languages from a new perspective. If you embrace the process, Lean will not only become a language you understand but a lens through which you view reasoning itself.
Let this introduction serve as the threshold. Beyond it lies a space where logic, mathematics, and programming converge; where precision becomes a form of elegance; and where the rigor of formal verification opens doors to new ways of understanding and constructing knowledge. Lean awaits, ready to guide you through a landscape where thought is crafted with exactness and where every idea, once expressed, stands on foundations strong enough to endure.
1. Introduction to Lean: What is Lean and Why Learn It?
2. Setting Up the Lean Development Environment
3. Your First Lean Program: "Hello, World!"
4. Understanding Lean Syntax: Structure and Formatting
5. Lean Data Types: Basic Types and Simple Variables
6. Working with Numbers in Lean: Integers, Reals, and Arithmetic
7. Lean Logic: Boolean Types and Logical Operations
8. Control Flow in Lean: if Statements and Conditionals
9. Loops and Recursion in Lean: Basics of Iteration
10. Functions in Lean: Defining and Calling Functions
11. Basic Input and Output in Lean: Reading and Printing Data
12. Understanding Variables and Scope in Lean
13. Using let and def to Bind Values and Define Functions
14. Pattern Matching in Lean: Basic Techniques
15. Lean’s match Expression: Matching Values and Types
16. Introduction to Lists and Collections in Lean
17. Using Tuples in Lean: Pairing Data Together
18. Working with Strings in Lean: Basic String Operations
19. Understanding Lean’s Option and Either Types
20. Error Handling and Exceptions in Lean: Using Option and Result
21. Introduction to Lean’s Type System: Types and Type Inference
22. Defining and Using Custom Types in Lean
23. Working with Algebraic Data Types (ADTs) in Lean
24. Type Constructors in Lean: Option, List, and More
25. Advanced Functions in Lean: Higher-Order Functions
26. First-Class Functions in Lean: Passing Functions as Arguments
27. Lean's bind and map for Functional Programming
28. Polymorphism in Lean: Using Generics for Flexibility
29. Lean’s mutual Definitions: Interdependent Functions and Types
30. Modules and Namespaces in Lean: Organizing Code
31. Lean's inductive Types: Defining Recursive Data Types
32. Working with Lean’s Theorem Proving System
33. Introduction to Lean’s proof and theorem Syntax
34. Using Lean for Formal Verification and Proofs
35. Lean’s Mathematical Libraries: Basic Theorems and Definitions
36. Defining Recursive Functions in Lean
37. Working with Lean’s Built-in Libraries for Lists and Sets
38. Introduction to Lean’s nat (Natural Numbers) Type
39. Advanced List Operations in Lean: Mapping, Filtering, and Reducing
40. Lean’s predicate Logic: Using Functions to Model Conditions
41. Using Lean’s tactic Mode for Proof Development
42. Lean’s inductive Theorem Proving: Proof by Induction
43. Pattern Matching with match Expressions in Lean
44. Defining and Working with Complex Recursive Functions
45. Lean’s record Types: Modeling Structured Data
46. Understanding Lean’s Proof and Refinement System
47. Using Lean's meta Programming Capabilities
48. Introduction to Lean’s setoid Theory: Equivalence Relations
49. The Lean Community: Where to Find Resources and Support
50. Interfacing Lean with Other Languages: Calling C and Python
51. Advanced Theorem Proving in Lean: Tactics and Strategies
52. Advanced Recursive Types in Lean: Universes and Inductive Families
53. Lean and Dependent Types: Introduction and Benefits
54. Proving Properties of Functions in Lean
55. Constructing Proofs by Induction and Recursion in Lean
56. Lean’s Type-Level Programming: Using universe Polymorphism
57. Homotopy Type Theory in Lean: Introduction and Basics
58. Writing Efficient Lean Code: Performance Considerations
59. Formalizing Mathematics in Lean: Set Theory and Functions
60. Lean’s category Theory: Modeling Algebraic Structures
61. Building Interactive Proofs with Lean: Using tactic Mode
62. Using Lean for Formalizing Programming Language Semantics
63. Advanced Pattern Matching in Lean: Customizing Matching Behavior
64. Proving Correctness of Algorithms in Lean
65. Building a Custom Proof Engine in Lean
66. The Lean 4 Update: New Features and Improvements
67. Lean's quote and unquote: Meta-Programming in Lean
68. Writing Custom Proof Tactics in Lean
69. Building a Lean Proof Checker for Formal Verification
70. Implementing Real-World Algorithms in Lean
71. Advanced Lean Theorems: Formalizing Complex Mathematical Proofs
72. Implementing Proofs of Correctness for Sorting Algorithms
73. Working with Lean’s class System for Type Classes
74. Combining Lean with Other Formal Methods and Tools
75. Lean for Formal Software Engineering: Specification and Verification
76. Using Lean for Automated Theorem Proving in Cryptography
77. Building Advanced Data Structures in Lean: Trees and Graphs
78. Proving Termination and Complexity of Functions in Lean
79. Lean for Machine Learning: Formalizing Neural Networks and Models
80. Building a Proof-Assistive IDE for Lean
81. Using Lean in Collaborative Formal Verification Projects
82. Introduction to Lean’s algebra Library: Vector Spaces and Groups
83. Using Lean for Cryptographic Protocol Verification
84. Extending Lean’s Proof Engine with Custom Logic
85. Implementing Dynamic Typing in Lean: Advanced Type Manipulation
86. Proving Properties of Recursive Algorithms in Lean
87. Combining Lean and Coq for Advanced Formal Verification
88. Lean for Compiler Verification: Building a Formal Compiler
89. Formalizing the Lambda Calculus in Lean
90. Understanding Lean’s congruence Tactics: Advanced Proof Techniques
91. Formalizing the Zermelo-Fraenkel Set Theory in Lean
92. Advanced Category Theory in Lean: Monoidal Categories and Functors
93. Creating Formal Libraries in Lean for Mathematical Domains
94. Using Lean for Formal Verification in High-Assurance Systems
95. Lean for Parallel and Distributed Systems Verification
96. Working with Lean's Proof Search Capabilities
97. Applying Lean to Formalize and Prove Security Protocols
98. Combining Lean with Model-Checking Techniques
99. Writing Lean Libraries for Advanced Data Structures and Algorithms
100. The Future of Lean: Trends, Community, and New Directions in Formal Proofs