Logical Foundationsの2章まで読んだ&Coqこと始め雑感
Coqでソフトウェア工学の基礎をいろいろ証明するよという趣旨(多分?)のSoftware Foundationsというウェブ上で読める教科書シリーズがある。
softwarefoundations.cis.upenn.edu
第1巻Logical FoundationsはそのままCoqの教科書になっている。これを読み進めていきたい。
とりあえず最初の2章までできたので(ただし最後の問題はいったん飛ばした・・・)、復習がてら雑感をメモ。
Fixpoint
再帰的な関数をFixpoint
で定義できる。例えばペアノ数として定義されているcoqの自然数の足し算:
Fixpoint add (n : nat) (m : nat) : nat := match n with | O => m | S n' => add n' (S m) end.
coqでは停止性が自明な関数しか書けない。その自明性もかなり粗いチェックになっていて、再帰のたびに必ず構造的に小さくなっていく引数が必要になる。上記のadd
関数では停止するまで第一引数が必ず小さくなっていく(S
が一つずつ外されていく)。
こういう関数すら許容されない:
Fixpoint one (n : nat) : nat := match n with | O => one (S n) | _ => S O end.
one (S n)
と引数が大きくなるような再帰は(人間の目からは自明に停止する関数でも)弾かれてしまう。
この制約でどううまく記述していくかはまだ慣れが必要(第2章最後の演習問題がそこで詰まっている・・・)
tactics
Fixpoint
を含むプログラミング構文で記述した関数などについての命題をtacticsを組み合わせて証明していくのがCoqの主眼。
第2章までで出てくるtactics:
intros
simpl
reflexivity
rewrite
destruct
induction
assert
replace
今のところ出てきたtacticsだと、contextにH : c = true
が入っていてgoalもc = true
だとrewrite -> H. reflexivity
とやってgoalをtrue = true
に変換してから自明に左右が同値だと指摘しないといけないのだけど、非常に冗長。多分contextとgoalが同じだと指摘するtacticsがあるはず・・・
assert
とreplace
の使い分けがまだよくわかっていない。
assert
は同じ証明の中で複数回使いたいような(でも完全に別の証明に分けるほどではない)補題のためにあるのかな。
Logical Foundationsではassert
の方が先に出てくるので演習などでそっちを使いまくっていて、その後replace
を知って書き直して少し簡潔になったケースが多かった。
ただ、
coqでassertを使うと冗長だけどすぐにsubgoalの証明に移れて、replaceだとスッキリかけるけどsubgoalの証明がずっと先になるのが気になる・・・
— zehnpaard (@zehnpaard) March 31, 2021
こう書いたとおり、assert
を使うとその場で導入した補題をsubgoalとして証明するのに対して、replace
だとその正当性を証明するのは当面のsubgoalの後になるので、証明の流れがわかりにくい気がする。
spacemacs develop branch
Clojureの関係でspacemacsのdevelop branchに切り替えたらそれまで不安定だったcompany-coqの補完機能の問題が全部解決した(以前は行末をm.
で終わらせると自動的にmatch ....
に補完されるなど非常にめんどくさかった・・・)
多分この記事:
に書いた手動でいじらないといけない部分も全部解決しているっぽい。spacemacsはdevelop branchを使うのがかなりベターなようだ。
和、積の法則の証明
演習問題に出てきた、自然数の和と積に関して交換則、結合則、分配則などの証明:
Coq proofs for addition/multiplication laws based on Software Foundations · GitHub
ゲーム性が高くて非常に楽しい。