章一覧

タクティクと証明のステップ

どの命題からどの命題へ進むかを、明示します。

16.1 自動化してもゴールは隠さない

証明が長くなると、等式を一つずつ書き換える作業が増えます。Tauto のタクティクは、その作業の一部を、証明項に展開するマクロです。ただし、書き換えの前後にどの命題があるかは、必ず書きます。

by rewrite!(e): 元のゴール {
  ⊢ 次のゴール from 次の証明
}

これは書式を示す図です。実際のコードでは、「元のゴール」と「次のゴール」に a = b などの命題を書きます。書き換えのモチーフを推論させず、書かれた二つのゴールから一つに決まるようにするのが、Tauto の方針です。

16.2 実際の書き換えを見る

リポジトリの examples/nat/Add.taut に、加算の交換法則の証明があります。命題を数式のまま書ける構文マクロ nat! を使っています。

examples/nat/Add.taut
pub theorem add_comm(n: Nat, m: Nat): nat!(`$n + $m = $m + $n`) {
  match m: nat!(`$n + $m = $m + $n`) {
    Zero => Eq::sym(add_zero_l(n))
    Succ(k) => by rewrite!(add_succ_l(k, n)): nat!(`$n + ${Succ(k)} = ${Succ(k)} + $n`) {
      ⊢ nat!(`$n + ${Succ(k)} = ${Succ(k + n)}`) from succ_cong(add_comm(n, k))
    }
  }
}
✓ 検査を通過
✓ theorem add_comm : ∀ n: Nat. ∀ m: Nat. n + m = m + n

nat! は、a = b に展開されます。$n は Tauto の変数 n を式に埋め込む書き方で、式を埋め込むときは ${Succ(k)} のように波括弧で囲みます。このファイルは、import "Lib.taut"(nat! の定義)と、add_zero_l、add_succ_l、succ_cong を同じファイルで先に証明しています。

rewrite! が何をしたかは、: のあとの元の等式と、⊢ のあとの次の等式を見比べると読めます。どちらかが実際のゴールと一致しなければ、カーネルに受け入れられません。

16.3 nat_norm! は算術を正規化する

examples/nat/Add.taut
pub theorem add_left_comm(a: Nat, b: Nat, c: Nat): nat!(`$a + ($b + $c) = $b + ($a + $c)`) {
  by nat_norm!(): nat!(`$a + ($b + $c) = $b + ($a + $c)`) {}
}
✓ 検査を通過
✓ theorem add_left_comm : ∀ a: Nat. ∀ b: Nat. ∀ c: Nat. a + (b + c) = b + (a + c)

nat_norm! は、自然数の加算・乗算などからなる式を正規化して、左辺と右辺の一致を証明するタクティクです。算術に、任意の性質を無条件で与えるわけではありません。

動かすには、リポジトリのルートで次のように検査します。

tauto check examples/nat/Add.taut
ok      add_comm
ok      add_assoc
ok      add_left_comm

(実際には、そのほかの定理も並びます。)

16.4 なぜ自動化を信頼しなくてよいのか

マクロが間違った証明項を生成しても、カーネルの検査を通らなければ、定理は受理されません。タクティクに許されるのは、証明の候補を作ることです。その候補が正しいかどうかを決めるのは、カーネルです。

演習 16.1:「自動化がバグを含んでいても、カーネルが健全なら、誤った定理の受理を防げる」と言える理由を、一文で書いてください。