x = y型と型アノテーションで4択クイズ
概要
2回ハマったのでメモ。
eqの定義とパターンマッチ(の型アノテーション)の難しさのせいで、
eqがtransitiveであることの証明は意外に難しい。
クイズ
イコールの推移律(任意のx,y,zについて、x = y ⇒ (y = z ⇒ x = z))を
Coqで証明しようとしています。
以下のeq_transitive1からeq_transitive4のうち、正しいtermはいくつあるでしょう?
Variables (A : Type) (x y z : A). Definition eq_transitive1 : x = y -> y = z -> x = z := fun xey yez => match xey in (_ = t) return t = z with | refl_equal => yez end. Definition eq_transitive2 : x = y -> y = z -> x = z := fun xey yez => match xey in (t = _) return t = z with | refl_equal => yez end. Definition eq_transitive3 : x = y -> y = z -> x = z := fun xey yez => match yez in (_ = t) return x = t with | refl_equal => xey end. Definition eq_transitive4 : x = y -> y = z -> x = z := fun xey yez => match yez in (t = _) return x = t with | refl_equal => xey end.
説明
このクイズには二種類の説明がいる。
第一に、eqの型はこんな感じ
Check eq. (* eq : forall A : Type, A -> A -> Prop *)
なので、一見するとA型の第二引数とA型の第三引数は対称に見える。
が、実は第二引数はパラメータで、第三引数はindexである。
Print eq. (* Inductive eq (A : Type) (x : A) : A -> Prop := refl_equal : x = x *)
1つめの(x : A)がコロンの左側にあって、2つめのAがコロンの右側にあるのがその証拠。
パターンマッチのmatch ... in (constructor var [var...])では
変数にindexを束縛できるが
パラメータを束縛することはできない。
したがってeq_transitive2と4はこんなエラーで弾かれる。
Error: The parameters of inductive types do not bind in the 'return' clauses; they must be replaced by '_' in the 'in' clauses.
つまり「in (_ = t)」とは書けるが「in (t = _)」とは書けない。
第二に、eq_transitive1の(match xey in (_ = t) return t = z with ...)の型は
returnの中でtがyに束縛されてy = zになる。
また、| refl_equal => の中では、
return t = zはreturn x = zになり、x = z型を返さなくてはならない。
したがってeq_transitive1は2か所で型エラー。(先に後者でエラーになる)
結局、正解はeq_transitive3のみ。
束縛遅延
必ずyezのパターンマッチをしなければいけないなんて理不尽だ!
という場合、returnの中を関数型にすることで、
xeyからパターンマッチすることもできる。
Definition eq_transitive5 : x = y -> y = z -> x = z := fun xey => match xey in (_ = t) return (t = z -> x = z) with | refl_equal => fun xez => xez end. Definition eq_transitive6 : x = y -> y = z -> x = z := fun xey yez => (match yez in (_ = t) return (x = y -> x = t) with | refl_equal => fun xey => xey end) xey.
eq_transitive5は、(match xey in (_ = t) return (t = z -> x = z) with ...)は
全体としてy = z -> x = z型になり、
refl_equal =>の中ではreturn (x = z -> x = z)になるため
fun xez => xez(恒等関数)で型が合う。
returnの中を関数型にするのは
Certified Programming with Dependent Types
http://adam.chlipala.net/cpdt/
で"delay the binding" "delay arguments"などと呼ばれていたテクニック。
match...in...returnは厄介なので詳しい説明が欲しいところ。