computo ergo sumEnglish
この説明の全体

入口

  1. Lean とは何か
  2. 使い方の入口
  3. 有限の判定を decide に載せる
  4. 数え上げを Finset で書く
  5. 単射一本で上界を出す
  6. 級数と不等式
  7. 証明書を Lean に検査させる
  8. 通っても、言いたいことが言えているか
  9. 言明を読む手順
  10. Lean では現実的に厳しいもの
  11. よくある罠
  12. 用語集
  13. Lean の仕組み

Lean の仕組み — 来歴と、検証が働く仕組み

「証明を機械に確かめさせる」という考えは Lean より半世紀ほど古く、Lean はその系統の新しい一本です。ここでは、その考えがどこから来たか、Lean 自身がどう作られているか、そして検査のあいだに実際に何が起きているかを順に見ます。最後の節は、Lean を見たことがなくても読めるように書いてあります。

このページの順序
  1. 機械に証明を確かめさせる、という考えの来歴
  2. Lean の来歴
  3. Lean はどう組み立てられているか
  4. 検証の仕組み — 初心者の歩幅で
  5. 文献・出典

01

機械に証明を確かめさせる、という考えの来歴

この考えは、離れた場所から来た二本の筋が合流したものです。

一本目は、証明を機械で扱える対象として書けること。関数に型を付ける仕組みと、含意「A ならば B」を証明する仕組みが同じ形をしている——この対応は Curry–Howard 対応と呼ばれます。名前の一方は、1934 年の Curry の論文「組み合わせ論理における関数性」に由来します。この対応があると、証明は読んで納得するものではなく、型の合った項という具体的な書き物になります。

二本目は、確かめる側は小さいほうがよいこと。1972 年、Milner は Stanford で LCF という証明検査系を実装しました。LCF の系統では、証明を組み立てる道具(tactic)はメタ言語で自由に書ける一方、出来上がったものを認めるかどうかは小さな中核だけが決めます。道具がいくら増えても、信頼しなければならない部分は増えません。

この二本を最初に本格的に合流させたのが、de Bruijn の Automath です。数学の証明を書き下し、機械に検査させるための言語として設計され、1970 年に報告されました。数学の証明を機械検査にかけるための、最初の全面的な試みです。1970 年代には、Jutting が Landau の『解析学の基礎』をこの言語で検査しています。

ここから出てきた設計方針が、のちに de Bruijn の基準と呼ばれるものです。証明を「検査の途中経過」としてではなく、それ自体で完結した項として残し、小さな独立の検査器で読み直せるようにする。証明支援系を比較する古典的な一覧表では、これが「小さな証明核(証明対象を持つこと)」という一項目として立てられています。Lean の公式文書も同じことを述べています——「証明項は定理の真理性の十分な証拠であり、独立した検証にかけられる」。

型理論の側では、Martin-Löf の直観主義型理論(1975 年)が数学の基礎を型の上に置き直そうとし、それを踏まえて Coquand が 1985 年に Calculus of Constructions の最初の版を出しました。最初の実装は 1984 年に Huet と Coquand が始めたもので、その中核は Constructive Engine と呼ばれる型検査器でした。1989 年に Coquand と Paulin が帰納的定義を加え、Calculus of Inductive Constructions になります。これが Coq の論理であり、そして Lean の論理の土台でもあります。

この系統で実際に検査された大きな定理を、年表に混ぜて並べます。

年できごと
1934Curry「組み合わせ論理における関数性」。Curry–Howard の名の一方
1970de Bruijn が Automath を報告。証明を機械検査にかける最初の全面的な試み
1972Milner が Stanford で LCF を実装(「計算可能関数の論理——機械実装の記述」)
1970 年代Jutting が Landau の『解析学の基礎』を Automath で検査
1975Martin-Löf「直観主義型理論——述語的部分」
1984Huet と Coquand が Calculus of Constructions の実装を始める。中核は Constructive Engine と呼ばれる型検査器
1985Coquand が Calculus of Constructions の最初の版を示す
1988Coquand と Huet が Calculus of Constructions の論文を出す
1989Coquand と Paulin が帰納的定義を加え、Calculus of Inductive Constructions へ
2005 頃四色定理の証明が Coq で検査されたことが報告される
2013Feit–Thompson の定理(奇数位数定理)を Coq で検査した論文が出る。6 年の共同作業
2017Kepler 予想の証明を HOL Light と Isabelle で検査した論文が出る(Flyspeck)

Lean はこの表の続きに位置します。土台の型理論は Coq と同系統で、設計方針は LCF 系統の「小さな中核」を引き継いでいます。


02

Lean の来歴

Lean は 2013 年、Microsoft Research の de Moura が始めました。最初のコミットは同年 7 月です。以後、記法と実装は何度も入れ替わりました。

最初の公開版は 0.1(2014 年)です。版 0.2 は標準の論理に加えてホモトピー型理論のモードを持っており、Lean 2 という名の別リポジトリに凍結されています。Lean 3(2017 年 1 月)で利用者が増え、同年 7 月に数学ライブラリ mathlib が作られます。2018 年 4 月に Lean 4 の開発が始まり、2023 年 9 月に正式版が出ました。

Lean 4 の特徴のひとつは、Lean 自身で書き直されていることです。parser も elaborator も tactic も Lean で書かれており、利用者がそれらを Lean のまま拡張できます。前の版で C++ を書かないと足せなかった部分が、ライブラリと同じ言語で書けるようになりました。

mathlib のほうは、個人の作業ではなく共同体の作業として育ちました。設計の記録は 2019 年の論文にまとめられており、いまでは 100 万行を超える規模になっています。Lean 3 から Lean 4 への移植は 2023 年 7 月に完了しました。

同じ 2023 年 7 月、Lean の開発体制そのものが変わります。de Moura と Ullrich が Lean FRO(Focused Research Organization)を設立し、Lean は非営利組織の下で開発される道具になりました。

Lean を使った形式化で、規模の目印になるものを二つ挙げます。Liquid Tensor Experiment は、2020 年 12 月に「この定理を形式化できるか」という挑戦として出され、第一目標の証明が 2021 年 5 月 28 日に予告され、2022 年 7 月 14 日に全体が完了しました。多項式 Freiman–Ruzsa 予想のほうは、証明の論文が 2023 年 11 月に出た直後に形式化のプロジェクトが立ち上がり、第一段階は完了しています。

年月できごと
2013-07Lean のリポジトリに最初のコミット
2014-06Lean 0.1 公開
2015-01Lean を使った最初の大学の講義(Carnegie Mellon University)
2015-08CADE-25 で Lean の系統記述(system description)が出る
2017-01Lean 3.0 公開
2017-07mathlib(Lean 3 用)が作られる
2018-04Lean 4 の開発開始
2019-10mathlib の設計を述べた論文が出る
2020-12Liquid Tensor Experiment が挑戦として出される
2021-05mathlib4 のリポジトリが作られる
2022-07Liquid Tensor Experiment 完了
2023-07Lean FRO 設立。mathlib の Lean 4 への移植完了
2023-09Lean 4.0 正式公開
2023-11多項式 Freiman–Ruzsa 予想の形式化プロジェクト開始
2023-12フェルマーの最終定理の形式化プロジェクト開始
2025-01mathlib4 への貢献が 2 万件を超える

03

Lean はどう組み立てられているか

利用者が書いた文字列が定理として認められるまでに、Lean は次の段を通ります。

段すること
parser文字の列を構文木にする。利用者が新しい記法を足せるので、構文木の型はとても一般的
macro 展開書きやすくするための糖衣構文を、もっと基本的な構文に置き換える
elaborator利用者向けの構文を、核となる型理論の項に変える。省略された引数を補い、型クラスの実例を探し、by の中の tactic を走らせる
kernelelaborator が出した項が、型理論の規則に従っているかを検査する
compilerelaborate 済みの Lean のコードを、実行できるものに変える

ここで効いているのは、核となる型理論が、利用者が書く言語よりずっと単純であることです。公式文書の言い方では、「この核の理論はずっと単純であり、それによって信頼される kernel を非常に小さく保てる」。elaborator がどれだけ賢くなっても、kernel が読むのは単純な言語のままです。

compiler は検証の列に入っていません。実行できるプログラムを作るための別系統で、定理が正しいかどうかには関わりません。例外は native_decide で、そのときだけ compiler の出した結果が公理として論理の側に入ります(01)。

核の型理論のほうも、名前だけ押さえておきます。

依存型

型が、値に依存して決まる仕組みです。「長さ n のリストの型」のように、n を受け取ってから型が定まる。命題を型として書けるのは、この仕組みがあるからです。

帰納型

「こういう作り方でできるものが全部」と宣言して型を作る仕組みです。自然数なら「0 と、何かの次の数」。この宣言から、場合分けと帰納法の原理が機械的に生成されます。

宇宙

型そのものも項なので、「型の型」が要ります。これを段に分けたものが宇宙で、各段には水準(自然数)が付きます。どの宇宙も一つ上の宇宙の要素であり、ある宇宙の型が量化できるのは、命題を除いて、それより小さい宇宙の型だけです。だから「すべての型についての型」を素朴に作ることはできません。

定義的等しさ

「計算すれば同じものになる」という関係です。関数に引数を当てる(β)・定義された名前を中身に置き換える(δ)・帰納型の場合分けを進める(ι)・let で置いた名前を値に置き換える(ζ)。このほか、商型の還元と、関数・単一構成子の型についての η 同値、それに証明の無関係性(同じ命題の証明はどれも等しい)が含まれます。kernel が「両辺が同じ」と言うときは、この関係のことです。

最後にひとつ、区別を。mathlib は kernel の一部ではありません。ライブラリ——つまり Lean で書かれた定義と定理の集まりであって、立場としては利用者が書くファイルと同じです。mathlib に誤った証明が混ざったとしても、それが kernel を通っていることに変わりはなく、逆に mathlib を全部外しても kernel は同じように働きます。


04

検証の仕組み — 初心者の歩幅で

ここがこのページの中心です。Lean を触ったことがなくても追えるように、たとえ話から始めて、実物を一歩ずつ見ていきます。

窓口のたとえ

証明を記入済みの書類だと思ってください。命題は書類の様式で、証明はその様式を埋めた一枚です。

kernel は記入の規則だけを知っている窓口です。窓口は、書類が誰の手で書かれたか、手書きか印字か、下書きを何枚捨てたかを見ません。見るのは、様式の欄が規則どおりに埋まっているかだけ。埋まっていれば受け取り、埋まっていなければ突き返します。

この「見ないこと」が要点です。証明を組み立てる道具(tactic)も、探索のプログラムも、人の勘も、窓口の外側にあります。窓口が見るのは、最後に提出された一枚だけです。

ここまでで分かったこと:kernel は「どう作ったか」ではなく「出来上がりが規則に合っているか」だけを見る。

一歩目 — 計算で閉じる

いちばん小さい書類を見ます。

theorem two_add_two : 2 + 2 = 4 := rfl

: の後が様式(命題)、:= の後が記入(証明)です。rfl は「両辺を計算すると同じものになる」という一語の記入で、これを受け取った kernel は、実際に両辺を計算します。

計算の規則は、自然数の加法の定義そのものです。Lean にはこう入っています。

#check @Nat.add_zero
#check @Nat.add_succ
Nat.add_zero : ∀ (n : ℕ), n + 0 = n
Nat.add_succ : ∀ (n m : ℕ), n + m.succ = (n + m).succ

「何かに 0 を足すとそれ自身」と「何かに『次の数』を足すと、足した結果の次の数」。この二本しかありません。succ は「次の数」を作る操作で、2 + 2 の右の 2 は 0 の次の次です。だから kernel の手は三手で済みます。

example : 2 + 2 = Nat.succ (2 + 1) := rfl
example : 2 + 1 = Nat.succ (2 + 0) := rfl
example : 2 + 0 = 2 := rfl
example : Nat.succ (Nat.succ 2) = 4 := rfl

四行とも検査を通ります。一行目と二行目が Nat.add_succ、三行目が Nat.add_zero、四行目は「4 という書き方が 2 の次の次を指している」ことです。この四つをつなげると 2 + 2 から 4 に着きます。最初の定理で kernel がしているのは、これと同じことです。

ここまでで分かったこと:rfl を受け取った kernel は、定義の規則を何手か使って両辺を同じ形にする。

二歩目 — 「かつ」の証明は組

計算で閉じない命題も、同じ枠に収まります。「A かつ B」の様式を見ます。

#check @And.intro
@And.intro : ∀ {a b : Prop}, a → b → a ∧ b

読み方は「命題 a と b について、a の証明と b の証明を受け取ると、a ∧ b の証明になる」。つまり a ∧ b の証明とは、a の証明と b の証明の組です。

example (p q : Prop) (hp : p) (hq : q) : p ∧ q := ⟨hp, hq⟩

(hp : p) は「p の証明を hp という名前で受け取る」という宣言、⟨ ⟩ は組を作る記法です。片方に間違ったものを入れると、窓口は理由を添えて突き返します。

example (p q : Prop) (hp : p) : p ∧ q := ⟨hp, hp⟩
error: Application type mismatch: The last
  hp
argument has type
  p
but is expected to have type
  q
in the application
  ⟨hp, hp⟩

「二つ目の欄には q の証明が来るはずなのに、p の証明が入っている」。書類の不備の指摘です。rfl の側でも同じです。

example : 2 + 2 = 5 := rfl
error: Type mismatch
  rfl
has type
  ?m.16 = ?m.16
but is expected to have type
  2 + 2 = 5

この二つの指摘を出しているのは、正確には kernel ではなく elaborator です。elaborator は項を組み立てながら自分でも型を見ているので、不備はたいてい kernel に届く前に見つかります。kernel は同じ規則で、出来上がった項をもう一度通す最後の関門です。

ここまでで分かったこと:命題ごとに「どう記入すればよいか」が決まっていて、欄の型が合わなければ通らない。

三歩目 — 「ならば」の証明は関数

「A ならば B」の記入は、A の証明を受け取って B の証明を返す関数です。

example (p : Prop) : p → p := fun hp => hp

example (p q : Prop) (h : p → q) (hp : p) : q := h hp

一行目は「p ならば p」——受け取ったものをそのまま返す関数です。二行目は「p ならば q」の証明 h と p の証明 hp があれば q の証明が得られる、を h hp(関数に引数を当てる)と書いています。論理の一歩が、関数の呼び出しと同じ形をしている。これが Curry–Howard 対応の手触りです。

ここまでで分かったこと:含意の証明は関数であり、推論はその関数を呼ぶこと。

四歩目 — tactic で書いても、最後は項になる

実際の証明は、こんなに短くは書けません。そこで by の後に tactic を並べ、項を代わりに組み立ててもらいます。

theorem and_swap (p q : Prop) (h : p ∧ q) : q ∧ p := by
  constructor
  · exact h.2
  · exact h.1

constructor は「q ∧ p を作るには q と p が要る」と目標を二つに割る tactic、exact はその欄に手持ちのものを入れる tactic です。組み立てた結果は #print で見えます。

#print and_swap
theorem and_swap : ∀ (p q : Prop), p ∧ q → q ∧ p :=
fun p q h => ⟨h.right, h.left⟩

四行の tactic が消えて、一行の項が残りました。しかも中身は、二歩目と三歩目で見た形そのものです——引数を受け取る関数(fun)と、二つの証明の組(⟨ ⟩)。tactic はこの項を書くための道具であって、kernel が受け取るのはこの一行だけです。

ここまでで分かったこと:tactic は書く手間を減らす道具で、提出される書類は手で書いたものと同じ種類の項。

なぜ信じられるのか

ここまでを踏まえると、信じなければならないものと、信じなくてよいものが分かれます。

信じる必要があるものkernel の実装/受け入れている公理(01)/言明の書き方——その命題が言いたかったことか(08)
信じなくてよいものtactic と自動化/探索や絞り込みのプログラム/証明を書いたのが人か自動生成かという出所/エディタの表示。どれも項を作るだけで、通るかどうかは kernel が決める

それでも「kernel の実装」は残ります。そこで、出来上がった証明を読み直す手立てが用意されています。Lean の公式文書は、確かめ方を強さの順に並べています。

確かめ方何が分かるか
エディタの青い二重チェックその定理の言明が elaborate され、kernel が証明を受け入れたこと。日常の作業ではこれで足りる
#print axioms依拠している公理の一覧。sorry の穴、勝手に足した公理、native_decide はここに出る
leancheckerビルドで作られた .olean の中の宣言を読み直し、kernel に通し直す。Lean の道具立てに同梱されている
照合器と外部の検査器証明項を書き出し、Lean の kernel と別の実装の検査器の両方で通し、さらに証明された言明が手元の言明と一致するかを照合する

三つ目の leanchecker は Lean の kernel をもう一度走らせる道具なので、kernel 自体の不具合は捕まえられません。捕まえるのは、kernel の状態を扱う周辺の不具合や、メタプログラムが検査を迂回して宣言を足す類のことです。

四つ目が、いま用意されているいちばん強い形です。証明を隔離した環境で組み立て、証明項を書き出し、その外側で——つまり証明の側のプログラムが手を出せない場所で——Lean の kernel と、独立に書かれた別の検査器の両方に通します。公式文書が名を挙げている外部検査器は Rust で独立に実装されたもので、ほかにも検査器を並べて比べる場が作られています。これが、de Bruijn の基準が実際に払っている配当です。証明が項として残っているから、別の検査器で読み直せる。

それでも前提は残ります。公式文書はそれも列挙しています——Lean の論理そのものが健全であること、書き出しと照合の配管が正しいこと、隔離環境が破られないこと、用いたすべての検査器に同時に効く不具合が無いこと、そして言明に人の誤りや誤解を招く書き方が無いこと。

ここまでで分かったこと:信頼の対象は kernel と公理と言明だけに絞られ、しかも kernel は別の実装で読み直せる。

それでも間違えうる所

言明が、言いたいことと違う

いちばん多い形です。定義を弱く取る、量化の順を入れ替える、仮定をひとつ足す。どれも検査は通り、証明された内容だけが別のものになります。機械は「落とした条件」を教えません。実例は 08、受け取った側の確かめ方は 09 に。

公理を足した

Lean では axiom で新しい公理を宣言できます。足せば何でも証明できてしまうので、標準の三つ以外が出ていないことを #print axioms で見ます(01)。

sorry の穴

書きかけの箇所を認めさせる印です。警告は出ますが検査は通り、#print axioms に sorryAx として現れます。エラーだけを見ていると見落とします(01)。

native_decide

判定を機械語にコンパイルして走らせ、その結果を公理として受け入れます。信頼の範囲が compiler と、実行時の差し替え指定が付いた定義すべてに広がります。このサイトでは使いません(01)。

kernel の不具合

kernel は小さいとはいえプログラムです。実際に、kernel を別の言語で書き直す試みから型検査の不具合が見つかり、報告された当日に対処された例があります(let 束縛の型に現れる変数の扱いについてのもの)。この種の不具合は、上の表の四つ目——実装の違う検査器を並べる形——でこそ捕まります。

誰が何を保証するか

もの保証すること保証しないこと
kernel提出された項が、定義と公理から型の規則に従って組まれていることその言明が言いたかったことであること
tactic・自動化何も。項を作るだけ——(失敗しても命題が偽とは限らない)
#print axioms依拠する公理の一覧が漏れなく出ることその公理が妥当かどうか
外部の検査器Lean の kernel の実装に頼らずに項を読み直せることLean の論理そのものの健全性
書いた人・読む人——言明と定義が意図と合っているかは、人が確かめる仕事のまま

機械検査が動かしたのは、確かめる対象と確かめる手段の境目です。「証明が正しいか」は機械に渡せる。「その定理が言いたかったことか」は渡せない。後者が残ることは弱点ではなく、前者が片付いたおかげで前面に出てきた仕事です。


文献・出典

節もの出典
01Curry–Howard 対応の名と、Automath がそれを用いたこと/LCF と tactic とメタ言語/CoC と CIC の年と人Rocq(旧 Coq)参考マニュアル「Early history of Coq」
01Curry「Functionality in Combinatory Logic」(1934)Proceedings of the National Academy of Sciences, 1934 年 11 月
01de Bruijn「The mathematical language AUTOMATH, its usage, and some of its extensions」(1970)Symposium on Automatic Demonstration, Lecture Notes in Mathematics
01Milner「Logic for Computable Functions: Description of a Machine Implementation」(1972 年 5 月)Stanford Artificial Intelligence Project, Memo AIM-169 / STAN-CS-72-288
01Martin-Löf「An Intuitionistic Theory of Types: Predicative Part」(1975)Logic Colloquium '73
01Coquand・Huet「The calculus of constructions」(1988)Information and Computation
01de Bruijn の基準という呼び名と、「小さな証明核」という項目立てWiedijk「The Seventeen Provers of the World」の比較表と脚注
01四色定理の Coq による検査Gonthier「A computer-checked proof of the Four Colour Theorem」(Microsoft Research Cambridge)
01Feit–Thompson の定理、6 年の共同作業Gonthier ほか「A Machine-Checked Proof of the Odd Order Theorem」(ITP 2013)
01Kepler 予想を HOL Light と Isabelle で検査Hales ほか「A formal proof of the Kepler conjecture」Forum of Mathematics, Pi(2017)
02Lean の年表(最初のコミットから現在まで)Lean FRO「A Brief History of Lean」
022013 年開始・小さな信頼される kernel・依存型理論de Moura ほか「The Lean Theorem Prover (System Description)」(CADE-25, 2015)
02Lean 4 が Lean 自身での書き直しであること・parser と elaborator を利用者が拡張できることde Moura・Ullrich「The Lean 4 Theorem Prover and Programming Language」(CADE 28, 2021)
02Lean 0.2 が標準モードとホモトピー型理論モードを持つことLean 2 のリポジトリ(凍結済み)の記述
02mathlib の設計と共同体の組織the mathlib Community「The lean mathematical library」(CPP 2020)
02mathlib が 100 万行を超えることLean 公式サイトの紹介
02Liquid Tensor Experiment の日付(2020-12 の挑戦・2021-05-28 の予告・2022-07-14 の完了)同プロジェクトのリポジトリの記述
02多項式 Freiman–Ruzsa 予想の証明の論文と、形式化の第一段階の完了Gowers・Green・Manners・Tao「On a conjecture of Marton」/同プロジェクトのリポジトリの記述
03parser から compiler までの五段と、それぞれの役割/「核の理論はずっと単純であり、それによって kernel を非常に小さく保てる」Lean 言語参考マニュアル「Elaboration and Compilation」
03依存型・帰納型・宇宙・商/定義的等しさ(β・δ・ι・ζ・商の還元・η・証明の無関係性)/「証明項は定理の真理性の十分な証拠であり、独立した検証にかけられる」Lean 言語参考マニュアル「The Type System」
04確かめ方の四段とそれぞれが守る範囲/残る前提の一覧/外部検査器が Rust で独立に実装されていることLean 言語参考マニュアル「Validating a Lean Proof」
04leanchecker が Lean の道具立てに同梱されていること旧 lean4checker のリポジトリの告知と、手元の Lean 4.33.1 の同梱物
04native_decide が compiler と差し替え指定付きの定義に信頼を広げることLean 言語参考マニュアル(decide の説明)
04kernel の型検査の不具合が見つかり同日に修正された例(let 束縛の型の変数の扱い)Lean の課題票 10475/kernel を別言語で書き直す試みが挙げている不具合一覧
04このページのコード片と出力Lean 4.33.1(leanprover/lean4:v4.33.1)+ mathlib。すべて手元で検査し、出力をそのまま写した。手順は 02

Lean の初歩は 01 Lean とは何か、手元で動かすところは 02 使い方の入口、語の意味は 12 用語集。

改訂 2026-09-20:新設。