Lean 4(元)编程 Cookbook

从函数应用构造表达式🔗

常量表达式🔗

最简单的表达式是常量。它们可以用 mkConst 函数构建,该函数接受一个常量的名字,返回表示该常量的表达式。例如,mkConst ``Nat 返回一个表示自然数类型的表达式。

直接应用🔗

构建表示函数应用的表达式,最简单的方式是使用 mkApp 函数,它接受一个函数表达式和一个参数表达式,返回一个表示把该函数应用到该参数的表达式。例如,我们可以按如下方式为 1 构建一个表达式:

open Lean in def oneExpr : Expr := mkApp (mkConst ``Nat.succ) (mkConst ``Nat.zero)

对于有多个参数的函数,我们可以使用 mkAppN,它接受一个函数表达式和一个参数表达式列表。例如,我们可以按如下方式为 2 构建一个表达式:

open Lean in def twoExpr : Expr := mkAppN (mkConst ``Nat.add) #[oneExpr, oneExpr]

带隐式参数、类型类等的函数应用🔗

虽然用 mkAppmkAppN 可以构建简单的表达式,但这些函数不处理隐式参数、类型类实例、宇宙层级、合一(unification)或 Lean 精译过程的其他特性。要构建正确处理这些特性的表达式,我们可以使用 mkAppM 函数,它接受一个函数的名字和一个参数表达式列表,返回一个表示把该函数应用到这些参数的表达式,同时正确处理隐式参数和类型类实例。

例如,我们可以用 mkAppM 按如下方式为对应于 Add.add 1 12 构建一个表达式:

open Lean Meta in def twoExprM : MetaM Expr := do mkAppM ``Add.add #[oneExpr, oneExpr]

还有一个相关的函数 mkAppM',它的第一个参数是表达式而不是名字。如果需要更精细地控制哪些参数应被推断、哪些应被显式给出,则有一个函数 mkAppOptM,它接受一个 Option Expr 数组,其中 none 表示该参数应被推断,而 some e 表示该参数应被显式给出为 e

例子:加法的交换律🔗

作为使用 mkAppM 的一个例子,我们可以为自然数加法的交换律构建一个表达式,它断言对所有自然数 ab 都有 a + b = b + a。我们先为自然数构建表达式,再为加法交换律这个命题构建表达式,最后用 Nat.add_comm 为该命题的一个证明构建表达式。

open Lean Meta in def natExpr (n : Nat) : Expr := match n with | 0 => mkConst ``Nat.zero | Nat.succ m => mkApp (mkConst ``Nat.succ) (natExpr m)

接下来我们为加法交换律这个命题构建一个表达式:

open Lean Meta in def addCommPropExpr (a b : Nat) : MetaM Expr := do let aExpr := natExpr a let bExpr := natExpr b let addAB mkAppM ``Add.add #[aExpr, bExpr] let addBA mkAppM ``Add.add #[bExpr, aExpr] mkAppM ``Eq #[addAB, addBA]

最后,我们可以用 Nat.add_comm 为该命题的一个证明构建表达式:

open Lean Meta in def addCommProofExpr (a b : Nat) : MetaM Expr := do let aExpr := natExpr a let bExpr := natExpr b mkAppM ``Nat.add_comm #[aExpr, bExpr]

我们可以检查这个证明表达式的类型确实是加法交换律这个命题。为此,我们用 inferType 函数推断证明表达式的类型,并用 isDefEq 检查它与命题表达式在定义上相等:

open Lean Meta in def checkAddCommProof (a b : Nat) : MetaM Bool := do let proofExpr addCommProofExpr a b let proofType inferType proofExpr let propExpr addCommPropExpr a b isDefEq proofType propExpr true#eval checkAddCommProof 2 3 -- true