Isabelle/HOL — 让程序证明像写数学论文一样可读
是什么
Isabelle/HOL 是一个让你用数学方式证明程序正确的工具。这本 2002 年 Nipkow-Paulson-Wenzel 的教科书,把 1990 年代积累的所有”怎么用机器证明”的经验,第一次系统地写给”会编程但不一定会数学”的工程师。
日常类比:菜谱审核员。你写了一份”做蛋糕”的菜谱,Isabelle 不是替你做蛋糕,而是逐字检查”打蛋打到 200 圈”这一步会不会让面糊塌——它把你的菜谱变成数学公式,再机械验证每一步推理对不对。
theorem add_commutes: "(x::nat) + y = y + x"proof (induct x) case 0 show ?case by simpnext case (Suc n) thus ?case by simpqed上面 6 行:第 1 行是要证的命题(自然数加法可交换),后 5 行是结构化证明(按 x 归纳,分 0 和 Suc n 两种情况,每步用 simp 自动化)。这种”像论文一样读得通”的写法,就是这本书最重要的贡献:Isar 语言。
为什么重要
不理解 Isabelle/HOL,下面这些事说不清:
- 为什么 seL4 微内核(NICTA 2009)敢说”内核没有 bug”——它的 10k 行 C 代码配了 ~200k 行 Isabelle/HOL 证明,验证从机器码到规范的每一层等价
- 为什么 CakeML 编译器号称”端到端被证明正确”——整个编译链(parser / type checker / 后端)的正确性定理全在 Isabelle 里跑过
- 为什么 lean-prover / Coq / Isabelle 三家长期共存——Coq/Lean 走依赖类型偏数学家,Isabelle 走 LCF + 强自动化偏程序员
- 为什么 Sledgehammer 能把一个看起来需要人手凑半小时的引理 5 秒解决——它把目标翻成 SMT 丢给 z3-2008 等求解器跑
核心要点
Isabelle/HOL 的设计可拆成 4 个层次:
第 1 层:LCF 内核(可信基只有几百行)
LCF approach 是 Robin Milner 1972 的发明:内核里只暴露一个抽象类型 thm,加几条原始推理规则。所有更高级的策略(simp / auto)最终都派生回这几条规则。意思是:整个系统可信不可信,只看那几百行内核——其余几十万行写错也只会导致”证明失败”,不会让假定理被接受。
类比:央行印钞机。机器本身只能按一种方式造钞,所有理财产品的合规性最终都回到那台机器上。
第 2 层:HOL 高阶逻辑(Church 简单类型论)
HOL = Higher-Order Logic。基础就是 Church 1940 的简单类型 λ-演算 + 选择公理 + 无穷公理。“高阶”指:函数本身能当变量传(你可以量词化”所有 P : nat → bool 满足……”)。和 hindley-milner 同源,但 HOL 加了量词和经典逻辑公理。
注意:HOL 不是依赖类型。你不能写 Vec n α(长度进类型)。要表达”长度为 n 的列表”,得用谓词 length xs = n 或者 record——这是 Isabelle 和 lean-prover 体感最大的差异。
第 3 层:Isar 结构化证明语言
这是 Markus Wenzel 1999 PhD 的发明,也是这本书最大的”工程贡献”。1990 年代主流证明器都用 tactic 脚本——一长串 apply (rule foo) apply (auto simp: bar) apply blast,谁都看不出第 7 步在干嘛。Isar 把证明改成”have ... show ... by ...”块结构,每一步显式写出当前状态:
proof - have step1: "x > 0" by simp have step2: "x + 1 > 1" using step1 by linarith show ?thesis using step2 by autoqed读起来就像数学论文。维护性高一个数量级。
第 4 层:自动化策略
- simp:条件重写。把方程当重写规则,朝某个方向化简。对绝大部分代数恒等式够用。
- auto:simp + 命题逻辑搜索 + 一阶推理混合,“先试它再说”
- blast:纯一阶推理,速度快但范围窄
- Sledgehammer(书中提到,2007 年后才成熟):把当前目标翻成 FOL,并发跑 z3-2008 / CVC4 / E / Vampire 几个外部求解器,找到证明后回译成 metis/smt 调用——这是把 nieuwenhuis-dpll-t-2006 的 DPLL(T) 真正接进交互式证明的关键工具
实践案例
案例 1:用 Hoare 三元组证一段循环代码
hoare-logic 的 {P} S {Q} 三元组在 Isabelle/HOL 里是深度嵌入的:
lemma "VARS x y {x = X ∧ y = Y} WHILE y ≠ 0 INV {x * y = X * Y - r} DO r := r + x; y := y - 1 OD {r = X * Y}" apply vcg apply auto doneVARS / INV / WHILE 是 Isabelle 内置的 Hoare 语法。vcg 自动生成验证条件(VC),auto 把剩下的算术义务清掉。整个流程把”写循环不变式 + 推 VC + 证 VC”拆成三个独立活儿。
案例 2:seL4 怎么用 Isabelle 证一个内核
seL4 ~10k 行 C 代码,对应 ~200k 行 Isabelle 证明。证明分三层:
- 抽象规范(abstract spec):内核要做什么,约 2k 行 Isabelle 函数
- 可执行规范(executable spec):和 C 数据结构对齐的精化
- C 代码本身:通过 AutoCorres 工具自动翻成 Isabelle 函数
证明这三层两两 refinement(精化),加上抽象层满足”capability 安全模型”。整套花了 NICTA 团队约 25 人年。这是工业证明规模的天花板参考。
案例 3:Sledgehammer 把不会证的引理外包
lemma tricky: "∀x. P x ∧ Q x ⟹ ∃y. P y" sledgehammer (* 输出:Try this: by metis 或 by (smt z3) *)你输入 sledgehammer,几秒后系统给出”Try this: by metis”。点进去定理就证完了。背后是 Z3 在跑——SMT 求解器接受 FOL 形式,证完后给一个核心引理列表,Isabelle 用 metis(一阶 resolution)把这个引理列表重新组装成 LCF 内核能信的证明。
踩过的坑
-
tactic 脚本写久了就成黑盒:
apply (auto simp: foo bar baz)一行,半年后看完全不知道在干啥。Isar 显式写中间have才能维护。 -
Sledgehammer 不可控:一个项目 1000 个证明,每个
by metis跑 5 秒就是 80 分钟 CI。生产代码要把 sledgehammer 找到的证明替换成更快的 simp/auto。 -
公理加错全盘崩塌:HOL 是经典逻辑,加一条矛盾公理(principle of explosion)整个理论可证一切。Isabelle 提供 Nitpick / Quickcheck 跑反例搜索兜底——证明前先 nitpick 看看有没有反例。
-
类型类不是 Haskell 类型类:Isabelle 有 axiomatic type classes(Wenzel 1997)但语义和 Haskell 不一样,是逻辑层的”假定 α 满足这些公理”。新人容易混淆。
适用 vs 不适用场景
适用:
- 大规模程序正确性证明(操作系统 / 编译器 / 安全协议)——seL4 / CakeML / 各种密码协议都选 Isabelle
- 需要强自动化的工程导向证明——Sledgehammer + simp + auto 比 Coq tactic 上手平缓
- 嵌入式领域语义(写 Hoare logic / 操作语义 / 类型系统)——HOL 的简单类型够用且工具成熟
不适用:
- 需要依赖类型 / 同伦类型论 / Univalence——选 Coq / lean-prover / Agda
- 需要可执行的程序提取(Coq 的 Extraction 更成熟,Isabelle 也有 code generation 但不如 Coq 主流)
- 想在工业代码里加几个 spec 就跑——Isabelle 重型、入门曲线陡,轻量场景选 Dafny / F*
学到什么
- 可信基越小越好:LCF 把内核压到几百行,几十万行外围都可任意写,错了只会”证明失败”。这是计算机科学里”trusted computing base”思想最干净的体现。
- 工程证明 ≠ 写论文:Isar 的发明承认”证明也是要被维护的代码”,可读性比简洁性重要。这套思想后来影响了 lean-prover 4 的设计。
- 自动化的极限是把目标外包:Sledgehammer 不试图自己更聪明,而是把目标翻成 SMT 丢给 z3-2008 这类专家求解器,再把答案翻译回 LCF 内核能信的形式。这是”专业化外包”在证明领域的胜利。
- HOL 用 50 年没过时:Church 1940 的简单类型论 + 选择公理这套基础,从 HOL88(Gordon)到今天的 Isabelle/HOL,证明者发现”够用 + 自动化好做”比”表达力顶满”更划算。
历史小故事(可跳过)
- 1972:Robin Milner 在 Stanford 写 LCF(Logic for Computable Functions),首次提出”内核 + 派生规则”。
- 1986:Larry Paulson 在 Cambridge 写出 Isabelle,但当时是 generic logical framework,能宿主多种逻辑(HOL / ZF / FOL)。
- 1989:Mike Gordon 在 Cambridge 把 HOL88 单独抽出。Isabelle/HOL 最终在 90 年代初汇合。
- 1999:Markus Wenzel 在 TU München 完成 Isar PhD,把”tactic 黑盒”翻新成”可读的结构化证明”。
- 2002:Nipkow-Paulson-Wenzel 合写本书,把 Isabelle/HOL 从研究工具推成可教学的工程工具。
- 2009:seL4 SOSP 论文发布,证明系统第一次进操作系统主流视野。
延伸阅读
- 教科书 PDF:Concrete Semantics with Isabelle/HOL(Nipkow & Klein 2014,比 2002 版更新更易上手)
- 视频:Tobias Nipkow — Programming and Proving in Isabelle/HOL
- seL4 主页:sel4.systems(含证明仓库与 SOSP 论文)
- Sledgehammer 论文:Blanchette et al., “Hammering towards QED”, JFR 2016
- hoare-logic —— Isabelle 里 Hoare 三元组的源头
- z3-2008 —— Sledgehammer 默认后端之一
- nieuwenhuis-dpll-t-2006 —— Z3 内核的 DPLL(T) 框架
- hindley-milner —— Isabelle 元语言 ML 的类型推导引擎
关联
- hoare-logic —— Isabelle/HOL 把
{P} S {Q}嵌入成内置语法 - z3-2008 —— Sledgehammer 把目标外包给 Z3 / CVC4
- nieuwenhuis-dpll-t-2006 —— Z3 的抽象基础,决定了 Sledgehammer 能跑多快
- hindley-milner —— Isabelle 内部 ML 元语言的类型系统
- lean-prover —— 同代竞争对手,依赖类型路线对照
- cakeml —— 全程用 Isabelle/HOL 证明的编译器
- lambda-calculus —— HOL 的 λ-演算基础
- system-f-reynolds-1974 —— HOL 的高阶量化思想前身