型クラスはレコードである
概要
Coqの型クラスはレコード。普通の型と同様に使える。
まとめ
リファレンスマニュアル(Chapter18)では分からないことがあったのでまとめておく。
リファレンスマニュアルには、quick referenceしか載せないから参考文献を読め、と書いてある。
Matthieu Sozeau and Nicolas Oury: First-Class Type Classes. In TPHOLs '08, 2008.
この文献は2008年の論文で、型クラスはCoqにあるもので表現できるのでそう実装したよ、ということが書いてある。まとめると、
- 型クラスとインスタンスは、dependent record型とその型をもつレコード(term)である。
- クラスメソッドはインスタンス(レコード型のterm)を取って1つのフィールドを返す射影関数である。インスタンス(および必要なパラメータ)についてimplicitなので、インスタンスを書かずとも(implicitな引数が全て推論できれば)呼びだすことができる。
- Instance宣言によってヒントデータベースtypeclass_instancesにインスタンスをapplyしろ、というヒントが追加される。このヒントはメソッドが呼び出されたときに自動tacticがインスタンスを探索するために使われる。
これらのことをリファレンスマニュアルの例(二項関係の反射律、推移律、前順序)で確認してみた。
知ったこと、わかったこと
- メソッドが1個の場合、型クラスをレコードではない型として定義できる。
(リファレンスマニュアルにはsingleton classと書いてある。)
インスタンスはtermで与えてもよいし、tacticでもつくることができる。
- 型クラス(レコード型)のコンストラクタは明示的に指定しない場合
Build_(typename)になる。
Instance宣言ではなぜかこのコンストラクタを使ってtermを与えることができなかった。Definitionなら通ったし、問題ないと思うのだけれど。
- あるクラスのメソッドの型が型クラスの場合、 ":"ではなく":>"を使うことで
クラスメソッドが内側インスタンス解決のためのヒントデータベースに登録される。
(この例では、Print Hint Reflexiveでapply PreOrder_Reflexive(1)が見えることから
PreOrderのクラスメソッドPreOrder_ReflexiveがReflexive解決に使われるとわかる)
- (Hintコマンドと同じく)Instance宣言でヒントデータベースに追加されたヒントは
宣言されたセクションのみで有効。
グローバルにしたい場合はGlobal Instanceコマンドを使う。
確認用ソースコード
Definition relation (A : Type) := A -> A -> Prop. Class Reflexive (A : Type) (R : relation A) := reflexivity : forall x, R x x. Print Reflexive. (* A singleton class is another name for a (parametrized) type *) (* Reflexive = fun (A : Type) (R : relation A) => forall x : A, R x x : forall A : Type, relation A -> Prop *) Print reflexivity. (* A method is a instance itself for a singleton class *) (* reflexivity = fun (A : Type) (R : relation A) (Reflexive : Reflexive A R) => Reflexive : forall (A : Type) (R : relation A), Reflexive A R -> forall x : A, R x x *) Class Transitive (A : Type) (R : relation A) := transitivity : forall x y z, R x y -> R y z -> R x z. Class PreOrder (A : Type) (R : relation A) :={ PreOrder_Reflexive :> Reflexive A R ; PreOrder_Transitive :> Transitive A R }. Print PreOrder. (* A type class is a record type. *) (* Record type has one constructor named Build_(typename) *) (* Record PreOrder (A : Type) (R : relation A) : Type := Build_PreOrder { PreOrder_Reflexive : Reflexive A R; PreOrder_Transitive : Transitive A R } *) Print PreOrder_Reflexive. (* A method is a projection function *) (* The method takes an instance as an implicit argument *) (* PreOrder_Reflexive = fun (A : Type) (R : relation A) (PreOrder0 : PreOrder A R) => let (PreOrder_Reflexive, _) := PreOrder0 in PreOrder_Reflexive : forall (A : Type) (R : relation A), PreOrder A R -> Reflexive A R Arguments A, R, PreOrder are implicit and maximally inserted *) (* no instance *) (* Definition t_112 : 1 + 1 = 2 := reflexivity 2. *) Section eq_reflexive. Instance eq_reflexive (A : Type) : Reflexive A (@eq A) := fun x => refl_equal x. End eq_reflexive. (* Though an instance is a global term, *) Print eq_reflexive. (* The effect of the instance declaration is limited to in the enclosing section.*) (* Definition t_112 : 1 + 1 = 2 := reflexivity 2. *) Existing Instance eq_reflexive. (* Implicit arguments are resolved thanks to declared instance *) Definition t_112 : 1 + 1 = 2 := reflexivity 2. (* Tactic is OK *) Instance eq_reflexive2 (A : Type) : Reflexive A (@eq A). unfold Reflexive; reflexivity. Defined. (* "Definition" doesn't register instances *) Definition eq_reflexive3 (A : Type) : Reflexive A (@eq A) := fun x => refl_equal x. Existing Instance eq_reflexive3. Instance eq_transitive (A : Type) : Transitive A (@eq A) := fun x y z xey yez => match yez in (_ = t) return x = t with refl_equal => xey end. Instance eq_transitive2 (A : Type) : Transitive A (@eq A). unfold Transitive. intros. rewrite H. assumption. Defined. Instance eq_preorder (A : Type) : PreOrder A (@eq A) :={ PreOrder_Reflexive := eq_reflexive A; PreOrder_Transitive := eq_transitive A }. (* One cannot use constructor for instance declaration, *) (* Instance eq_preorder2 (A : Type) : PreOrder A (@eq A) := Build_PreOrder A (@eq A) (eq_reflexive A) (eq_transitive A). *) (* though it is allowed for Definition command *) Definition eq_preorder2 (A : Type) : PreOrder A (@eq A) := Build_PreOrder A (@eq A) (eq_reflexive A) (eq_transitive A). Existing Instance eq_preorder2. Instance eq_preorder3 (A : Type) : PreOrder A (@eq A). intro; constructor. apply eq_reflexive. apply eq_transitive. Defined. (* Thanks to substructure (:>) declaration, one can use reflexivity and transitivity when there is a PreOrder instance *) Definition preorder_test {A}{R} {pre : PreOrder A R} := @reflexivity A. Definition preorder_test2 {A}{R}{pre : PreOrder A R} := @transitivity A. (* Instances are registered as hints by Instance or Existing Instance command. *) Print Hint Reflexive. (* ... In the database typeclass_instances: apply eq_reflexive3(0) apply eq_reflexive2(0) apply eq_reflexive(0) apply PreOrder_Reflexive(1) ... *) Print Hint PreOrder. (* ... In the database typeclass_instances: apply eq_preorder3(0) apply eq_preorder2(0) apply eq_preorder(0) ... *)