view presen/agda-prog.ind @ 12:5b51dffd2b19 default tip

add fig
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 11 Jan 2014 09:48:32 +0900
parents 0a2454365e55
children
line wrap: on
line source

-title:  Agda での Programming 技術

-author:  

--なんで、今、証明支援系なのか?

証明支援系の歴史は古い ( HOL, Coq, Agda も新しくない )

Haskell が流行ってるようだが…

     Haskell はMonad使いまくり

     Monadって何?

--この発表は...

Agdaをさんざん触って面白かった

  1部 Monad って何?
  2部 Agda って何?

--Monadって何?

Monad を解説しているサイトは多いが、多くは皮相的

元は圏論の概念

→ だったら、Monad を証明支援系を使って理解すれば良いんじゃないの?

とりあえず、目標は Monad の合成則の証明

--圏

Category とは、集合であるオブジェクト A と、オブジェクト間の写像である射 f の組。
    id(A) とは、A の要素を同じ要素に写像する射で必ず存在する 

f: A->B, g: B->C に対して、射の合成がある。
    g ○ f : A->C

合成則

    h ○ (g ○ f )  = (h ○ g) ○ f )

A->A への単位射がある。同じ値を同じ値に返す射。
    id_a : A->A

これだけ。(あとは、Functor, Natural Transformation, Adjunction, Monad ... )

--射と合成則

<center><img src="fig/arrow.jpg"></center>


--圏と関数型プログラミングとの関係

射が関数 ( = 関数型プログラミングのプログラム)

対象が型 ( 射は、型→型の写像)

これだけ。(あとは、Functor, Natural Transformation, Adjunction, Monad ... )

--圏としての型

例えば Integer は、Integer から Integer への関数がある。これを射と考える。

関数の合成則は成立しているし、単射もある。

型は圏でもある。

   Int → List

--Functor

Functor F とは、Category C から D への射

Cの対象aをDの対象a'に対応させる写像をFObjとする。
Cの射fをDの射f'に対応させる写像をFMapとする。

射の対応が、以下を満たす時に、F はFunctor と言う。

    (1) C の射 f:a->b を Dの射 F(f):F(a)->F(b) に割り当てる (FMap)
    (2) id(F(a)) = F(id(a))
    (3) f: a->b, g: b->c に対して、
        F(g) ○ F(f) : F(g○f)   分配法則

Functor は対象の写像FObjと、射の写像FMapの二つを持つ。

Functor の数学記法は polymorphic

--Constructor としての Functor

Integer と List はどちらも圏であり、
Integer から List を作る Constructor は、
Category から Category への Functor に相当する。

つまり、Haskell の data はFunctor になる。

    data List a = (::) a (List a)
           |      []

   data Nodes a = Node (Nodes a) (Nodes a)
       |       Leaf a

List や Node を Functor のFObj(対象の射)として考えることができる。
(Leaf や :: は constructor )

--FMap

Functor の射の写像は、fmap として(自分で)定義する。

     class Functor f where
        fmap :: (a->b) -> f a -> f b

    fmap f (Leaf a ) =  Leaf (f a)
    fmap f (Node  a b ) =  Node (fmap f a) (fmap f b)


fmap は要素の関数 g: a->b を List や Node の関数へ変換している。
この時、f は List や Node の要素を操作する関数。

            T(f)
      T ------------> T

は T の要素に f を作用させると考えて良い。

--Natural Transformation (自然変換)

Node から List への変換を考える。これは、Functor から Functor への射になる。

例えば、flatten ( 木を深さ順にリストにする) は、そういう射の例である。

Node の要素をIntegerからStringに変換するには show が使える。( show 1 = '1' )

ここで、

   flattern ( List.fmap show aNode ) = Node.fmap show ( flattern  aNode )

これを可換(commute)という。可換なFunctorからFunctorへの射をNatural Transformation
とう。

<center><img src="fig/flatten.jpg"></center>

--Natural Transformation

Functor で可換を書くと、

<center><img src="fig/nat.jpg"></center>

ここで、F,G は、データ型。F(a)/F(b)) は FObj 。F(f) / G(f) は FMap。

t は自然変換(引数は要素の型)

--Monad

Monad とは、Functor T と二つの自然変換η,μの三つ組  (T,η,μ) で、
以下の性質を満たすものである。

    T: A -> A             T は AからAへのFunctor
    η: 1_A -> T            ηは、a から T a への自然変換
    μ: T^2 -> T            μは T T a から T a への自然変換

さらに、Unity Law と呼ばる

   μ○Tη = 1_T = μ○ηT 

と、Association Law と呼ばれる以下の可換図を満たす。

<center><img src="fig/nat.jpg"></center>

--Kleisli 圏

上の Monad で、 a から T b への射を 新しい Kleisli 圏の射と考える。

この射の合成を、

  f : b -> T(c)
  g : c -> T(d)
  g * f = μ(d)T(g)f    

として定義する。

Monad の合成則 (g * f ) * h = g * ( f * h ) が成立する。

--Haskell での Monad

T はFunctorだからデータ型。メタデータだと思って良い。計算の詳細に関する
メタなデータと、元のデータを含むデータ型。

Haskell Monad はメタデータを追加して返す関数。 Reflection に相当する。

<center><img src="fig/Kleisli.jpg"></center>


例えば、IO Monad は、入出力するデータをメタデータとして返す。

--Monad の合成

メタデータの変化は自然変換 μ で決まる。

<center><img src="fig/join.jpg"></center>

合成則の証明は
<a href="fig/monad-assoc.png">煩雑 </a>

--Haskell 使うのに圏論とか必要なのか?

知らなくても困らない

Monad とか用語は理解できる

Functor と map はわかる

煩雑な証明を自分でやって理解する

そもそも証明自体が圏論( Curry Howard 対応)

Curry Howard 対応な証明支援系である Agda を使えば?

(これで1部終わり。長かった...)

--Curry Howard 対応

証明とλ計算式が対応する。対応は、扱う論理に寄って異なる

論理が複雑になる分、λ計算を拡張する必要がある

  → ラムダ計算の拡張の指針になる

証明する論理式は型に対応する

証明するべき式の型になるλ式を作れれば証明されたことになる

--Agda とは何か

Haskell実装、Haskellに似た構文

   indent による scope の生成

型と値

   : で型を定義
   = で値を定義

集合
    すべての値は集合

集合のレベル
    集合の集合はレベルが上がる

暗黙の引数

    {} で省略可能な引数を作れる

--λ

    module proposition where
    postulate A : Set
    postulate B : Set

集合であるA,Bを命題変数として使う。

    Lemma1 : Set
    Lemma1 = A -> ( A -> B ) -> B

これが証明すべき命題論理式になる。実際には式に対応する型。
    lemma1 : Lemma1

として、この型を持つ lambda 式を使えば証明したことになる。
以下のどれもが証明となり得る。

    -- lemma1 a a-b = a-b a
    -- lemma1 = \a a-b -> a-b a
    -- lemma1 = \a b -> b a
    -- lemma1  a b = b a


--data 

Haskell の data と同じ。

    infixr 40 _::_
    data  List  (A : Set ) : Set  where
       [] : List A
       _::_ : A → List A → List A


論理式の排他的論理和を表すのにも使える。

    data _∨_  (A B : Set) : Set where
      or1 : A -> A ∨ B
      or2 : B -> A ∨ B

or1 と or2 が Constructor である。

    Lemma6  : Set
    Lemma6 = B -> ( A ∨ B )
    lemma6 : Lemma6
    lemma6 = \b -> or2 b

これは、∨ の導入になっている。削除は、

    lemma7: ( A ∨ B ) -> A
    lemma7 = ?


--equality

さらに、data は等式を表すのにも使う。

    data _==_  {A : Set } :  List A → List A → Set  where
          reflection  : {x : List A} → x == x

直観主義論理には命題自体に真偽はない。なので、返すのは Set になっている。なので、二重否定は自明には成立しない。

--record

record は、論理積を作るのに使う。

    record _∧_ (A B : Set) : Set where
       field
          and1 : A
          and2 : B

∧の導入

    lemma2 : A → B → A ∧ B
    lemma2 a b =  record { and1 = a ; and2 = b }

∧の削除 ( open _∧_  が必要 )

    lemma3 : A ∧ B → A
    lemma3 a =  and1 a

record は、ある性質を満たす数学的構造を表すのに使う。

--Append の例

    postulate A : Set

    postulate a : A
    postulate b : A
    postulate c : A


    infixr 40 _::_
    data  List  (A : Set ) : Set  where
       [] : List A
       _::_ : A → List A → List A


    infixl 30 _++_
    _++_ :   {A : Set } → List A → List A → List A
    []        ++ ys = ys
    (x :: xs) ++ ys = x :: (xs ++ ys)


-- Append の実行

  Agda の実行は正規化
   Haskell と同じ

    l1 = a :: []
    l2 = a :: b :: a :: c ::  []

    l3 = l1 ++ l2


--Append の結合則の証明

証明するべき式 

    list-assoc : {A : Set } → ( xs ys zs : List A ) →
         ( ( xs ++ ys ) ++ zs ) == ( xs ++ ( ys ++ zs ) )


List の持つ性質を表す式

    data _==_  {A : Set } :  List A → List A → Set  where
          reflection  : {x : List A} → x == x

    cong1 :  {A : Set  }  { B : Set  } →
       ( f : List A → List B ) → {x : List A } → {y : List A} → x == y → f x == f y

    eq-cons :   {A : Set } {x y : List A} ( a : A ) → x ==  y → ( a :: x ) == ( a :: y )

--Agda での証明手順

(1) 証明するべき式を型として記述する

(2) 値を = ? と取り敢えず書く

(3) ? をλ式で埋めていく

(4) C-N で正しいかどうかをチェックする

    赤   ... 型エラー
    黄色 ... 具象化が足りない
    停止性を満たさなない

(5) data  は patern match で場合分けする

(6) 必要なら式変形を使う


--Haskell と Agda の違い

Haskell で結合則を証明できる?

Haskell は型に関数やデータ型の constructor を書けない

Haskell のプログラムは Agda のプログラムになる?

   float とかはない
   integer は、succesor で書く必要がある

--式変形

λ式では、人間に読める証明にならない

    ++-assoc A (x :: xs) ys zs = let open  ==-Reasoning A in
      begin -- to prove ((x :: xs) ++ ys) ++ zs == (x :: xs) ++ (ys ++ zs)                                                                                       
        ((x :: xs) ++ ys) ++ zs
      ==⟨ reflection ⟩
         (x :: (xs ++ ys)) ++ zs
      ==⟨ reflection ⟩
        x :: ((xs ++ ys) ++ zs)
      ==⟨ cong1 (_::_ x) (++-assoc A xs ys zs) ⟩
        x :: (xs ++ (ys ++ zs))
      ==⟨ reflection ⟩
        (x :: xs) ++ (ys ++ zs)





--いくつかの技術

等号

?

Unicode の記号

module

暗黙の引数が多すぎる

--Agda で証明できないこと

a ≡ b は証明できない

functional extensionarty

    -- Extensionality a b = {A : Set a} {B : A → Set b} {f g : (x : A) → B x} → 
          (∀ x → f x ≡ g x) → f ≡ g → ( λ x → f x ≡ λ x → g x )

実際には、

    postulate extensionality : { c₁ c₂ ℓ : Level} { A : Category c₁ c₂ ℓ } 
       → Relation.Binary.PropositionalEquality.Extensionality c₂ c₂


部分集合

    異なる集合の要素は異なるので、部分集合を作れない
    →  x ∈ P  を自分で定義する

合同性   x ≡  y ならば f x ≡  f y 。これは、_≡_  に付いてしか言えない。
Relation などで定義すると、合同性は自分で仮定あるいは証明する必要がある。


直観主義論理の限界

   あんまり良くわかりません

--圏論の定理の証明

  Monad の結合性
  Monad から作る Kleisli 圏
  Kleisli 圏から作る随伴関手
  随伴関手での Limit の保存
  米田のレンマ

--本の証明とAgdaの証明

    本の証明は、山登りの道標しか示さない

    Agdaの証明は、全部繋がっている

    Agda の証明は式なので対称性が目で見える

--定理を理解することと証明を理解すること

定理を理解するということは使い方を理解すること

定理は証明のショートカットでもある

証明を理解しても使いこなせない

--結局、Monad は役に立つのか?

メタプログラミング(IOや並列処理)として便利

構文やややうざい。C のプログラミングみたい。

結合則や Functor(fmapを持つ) は必須ではない

Monad の合成はさらに煩雑 (Beck Law)

Monad Transform も「一度Monadを分解して再合成」

圏論知らなくても使えるが、勉強すれば用語は納得できる

--結局、証明 は役に立つのか?

Monad に関しては役に立たない

圏論を理解するのは役に立つ

教科書は「道標のみ」

自分で山を登らないと。山を登るためのツール。

--Agda は面白い

詰将棋みたいなもの

学会でノートPCで内職しているのは Coq 

Agda は Coq よりもマイナー

Agda の情報が少ないので面白い

みんな Agda を使おう!

--おまけ

Agda は Emacs で使うもの

Vimmer にはつらすぎる (手がつる)

vim emulator を使うと幸せになれます