Upgrade to Pro — share decks privately, control downloads, hide ads and more …

型クラスと依存型のカルパッチョ、代数的構造を添えて

soukouki
January 21, 2024

 型クラスと依存型のカルパッチョ、代数的構造を添えて

2024-01-21のZli 大LTで発表したスライドです。

soukouki

January 21, 2024
Tweet

More Decks by soukouki

Other Decks in Technology

Transcript

  1. Numクラス class Num a where (+) :: a -> a

    -> a (-) :: a -> a -> a (*) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a 「数っぽいもの」を表すNumクラスは、以下の値や関数を持つような型の抽象を表し ます。 + , - , * の演算子 negate , abs , signum の関数 fromInteger の変換関数 そして、Integer型やFloat型は、Numクラスに属することを宣言(インスタンス化)する と、それらの型をNumクラスとして抽象的に扱えるようになります。 6
  2. manhattanDistance :: Num a => (a, a) -> (a, a)

    -> a manhattanDistance (x1, y1) (x2, y2) = abs (x1 - x2) + abs (y1 - y2) manhattanDistance (1, 2) (3, 4) -- 4 manhattanDistance (1.0, 2.0) (3.0, 4.0) -- 4.0 2つの点のマンハッタン距離を求める関数 manhattanDistance です。 Num型で抽象化して実装しているので、Int型でもFloat型でも使えます。 このように、型を抽象的に扱い、実装を簡単にするのが型クラスの魅力です。 7
  3. 一般的な言語でのモノイド モノイドとは、集合 と、 くっつける演算( を使って と書く) 任意の に対して、 が成り立つ(結合的) くっつけても何も起こらない値(単位元

    ) 任意の に対して、 が成り立つ が定義されている組のことです。[1] Haskellでは以下のように定義します。 -- Monoid型クラスの定義(説明用。現実のGHCのものとはちょっと違うので注意) class Monoid a where (<>) :: a -> a -> a -- 「くっつける」演算 mempty :: a -- 「くっつけても何も起こらない値」 しかし、足りないものがあると思いませんか? 8
  4. Coqでのモノイド Class Monoid (A : Type) : Type := {

    (* くっつける演算 op *) op : A -> A -> A; (* 単位元 e *) e : A; (* 「opが結合的である」という約束 *) monoid_assoc : forall x y z, op x (op y z) = op (op x y) z; (* 「eが単位元」という約束 *) monoid_e_left : forall x, op e x = x; monoid_e_right : forall x, op x e = x }. 12
  5. Instance BoolXorMonoid : Monoid bool := { (* くっつける演算はXOR *)

    op := xorb; (* 単位元はfalse *) e := false; (* XORは結合的という約束を与える(標準ライブラリで証明済み) *) monoid_assoc := fun a b c => eq_sym (xorb_assoc a b c); (* falseは単位元という約束を与える(標準ライブラリで証明済み) *) monoid_e_left := xorb_false_l; monoid_e_right := xorb_false_r }. boolのXOR演算はモノイドの性質を満たすので、それらをモノイドのインスタンスと して扱えるようになります。 13
  6. 継承 型クラスにも継承の仕組みがあります。Coqでは、群がモノイドであるといったふう に、あるクラスがスーパークラスの要素を要求する形で行われます。 群は、モノイドの要素に加えて、任意の要素 に対して、逆元 が存在するという条 件が追加されます。 Class Group A

    (M : Monoid A) : Type := { group_inv : forall x, exists xi, op xi x = e /\ op x xi = e; }. ここでは、モノイドを拡張して群を定義してみました。モノイドの諸性質に加え、逆 源に関する公理を追加することで群を定義します。 14
  7. Lemma xorb_inv : forall x, exists xi, xorb xi x

    = false /\ xorb x xi = false. Proof. move=> x. exists x. split; apply xorb_nilpotent. Qed. 補題 xorb_inv を証明、つまり「約束」が守られることを確認します。 (* boolとXorの群 *) Instance BoolXorGroup : Group BoolXorMonoid := { group_inv := xorb_inv }. インスタンス化する際にその「約束」を渡すことで、boolとXORを群として扱えるよ うになります。 そして型クラスの仕組みによって、他の群を前提としたプログラムや証明に対して boolとXOR演算を適用できます。 15
  8. 型クラスを使った証明 モノイドの単位元が1つだけ存在することの証明です。 を証明しています。 Theorem monoid_exists_unique_e A (M : Monoid A)

    : forall e', (forall x, op e' x = x) -> e' = e. Proof. move=> e' H1. rewrite -(monoid_e_right e'). by rewrite -{2}(H1 e). Qed. monoid_e_right ( が単位元という約束)を使っているのが見えます。 16
  9. 群の逆元がただ1つ存在することの証明です。 exists! で1つだけ存在することを表し ます。 Theorem group_exists_unique_inv A (M : Monoid

    A) (G : Group M) : forall x, exists! xi, op xi x = e /\ op x xi = e. Proof. move=> x. rewrite -(unique_existence _). split. - case (group_inv x) => xi [Hxil Hxir]. exists xi. by split. - move=> a b [Hal Har] [Hbl Hbr]. rewrite -(monoid_e_right a) -Hbr. rewrite monoid_assoc. by rewrite Hal monoid_e_left. Qed. group_inv (逆元が存在するという約束)、 monoid_assoc (演算が結合的という約束)な ど、様々な約束を用いて証明しているのがわかります。 17
  10. 群の逆元がただ1つ存在するので、決定性の値取り出し公理を用いてinv関数を定義し ます。また、inv関数を使った という性質も証明します。 Definition inv A (M : Monoid A)

    (G : Group M) : A -> A := fun x => proj1_sig (constructive_definite_description _ (group_exists_unique_inv G x)). Lemma inv_sort A (M : Monoid A) (G : Group M) : forall x xi, xi = inv G x <-> op xi x = e /\ op x xi = e. Proof. move=> x xi. unfold inv. move: (constructive_definite_description _ (group_exists_unique_inv G x)) => He. split. - case (proj2_sig He) => [Hel Her] ->. by split. - move => Hxe. move: (group_exists_unique_inv G x). rewrite -unique_existence => [[_ Huniq]]. apply Huniq => //. by apply (proj2_sig He). Qed. Lemma op_inv_left A (M : Monoid A) (G : Group M) : forall x, op (inv G x) x = e. Proof. move=> x. by apply (inv_sort G x (inv G x)). Qed. Lemma op_inv_right A (M : Monoid A) (G : Group M) : forall x, op x (inv G x) = e. Proof. move=> x. by apply (inv_sort G x (inv G x)). Qed. 18