Mercurial > hg > Papers > 2015 > atton-thesis
view category.tex @ 29:ed97e5de348d
Add todo list
author | Yasutaka Higa <e115763@ie.u-ryukyu.ac.jp> |
---|---|
date | Thu, 12 Feb 2015 11:28:50 +0900 |
parents | fc44782929a7 |
children | 113b49263d40 |
line wrap: on
line source
\chapter{Categorical Definitions of Monad} \label{chapter:category} \ref{chapter:delta}章ではプログラムの変更がメタ計算としてMonadを用いて定義可能であることを示した。 ここで Monad の定義と要請されるMonad則について述べる。 また、定義は Monad の解説に必要な部分についてのみ解説する。 % {{{ Category \section{Category} \label{section:category} まずは Monad の定義に必要な Category (圏)について述べる。 category は2つの要素を持つ。 \begin{itemize} \item object (要素) \item morphism (射, arrow) object から object へのマッピング。 morphism によってマップされる対象を domain と呼び、マップした先の対象を codomain と呼ぶ。 つまり domain から codomain への object をマップするものが morphism である。 domain A と codomain B を持つ morphism f は式\ref{exp:morphism}のように書くこととする。 \begin{equation} f : A \rightarrow B \label{exp:morphism} \end{equation} \end{itemize} そして category は object と moprhism に対して2つの法則を満たす。 \begin{itemize} \item 全ての object について identity mapping が存在する identity mapping とは domain と codomain が同じ morphism のことである。 identity mappping は id と略する。 id は全ての object に存在するため、 object A に対する id は $ id_A $ と書く。 \item 同じ obejct が domain と codomain になっている2つのmorphismは合成することができ、合成の順番は結果に影響しない。 morphism の合成には記号 $ \circ $ を用いる。 object B から C への morpshim f と object A から B への morphism g があった時、 f と g を合成すると式\ref{exp:morphism_compose}となる。 \begin{equation} f \circ g : A \rightarrow C \label{exp:morphism_compose} \end{equation} 改めて、morphism の合成の順序が結果に影響しないことを式にすると式\ref{exp:morphism_composition_law}のようになる。 \begin{eqnarray} \label{exp:morphism_composition_law} f : C \rightarrow D \nonumber \\ g : B \rightarrow C \nonumber \\ h : A \rightarrow B \nonumber \\ (f \circ g) \circ h = f \circ (g \circ h) : A \rightarrow D \end{eqnarray} \end{itemize} 例えば、object A,B と A から B への moprhism を持つ category は図\ref{fig:simple_category}のようになる。 \begin{figure}[htbp] \begin{center} \includegraphics[scale=1.0]{fig/simple_category.pdf} \caption{object A,B と morpshim f を持つ category} \label{fig:simple_category} \end{center} \end{figure} なお、id は全ての object に存在するために省略して書くことが多いため、これからは省略する。 object を点、morphism を線とした図を commutative diagram (可換図式) と呼ぶ。 ある object から object への morphism の合成の path に関わらず結果が同じである時、その図を commutative (可換)と呼ぶ。 例えば、式\ref{exp:morphism_composition_law}の category の commutative diagram は図\ref{fig:morphism_composition_law}のようになる。 \begin{figure}[htbp] \begin{center} \includegraphics[scale=0.8]{fig/morphism_composition_law.pdf} \caption{式\ref{exp:morphism_composition_law} の morphism の合成法則の category がなす commutative diagram} \label{fig:morphism_composition_law} \end{center} \end{figure} commutative diagram が commutative である時、moprhism の合成順序が異なっても結果が同じであることを利用して等価性の証明を行なうことを diagram chasing と呼ぶ。 ある性質を category に mapping し、diagram chasing を用いて証明を導くことで性質を解析していく。 % }}} % {{{ Functor \section{Functor} \label{section:functor} \ref{section:category} 節では category を定義した。 \ref{section:functor} 節では category から category への mapping である Functor (関手)について解説する。 functor F とはある category C から D への mapping である。 functor F を用いて category C の object もしくは morphism である x を category D の object もしくは morphism に対応させることを式\ref{exp:apply_functor}のように記述する。 \begin{equation} F(x) \label{exp:apply_functor} \end{equation} functor は以下の functor 則を満たす。 \begin{itemize} \item id を保存する category C における object A への id $ 1_A $ は F A においても id として振る舞う。 \begin{equation} F(1_A) = 1_{F(A)} \end{equation} \item morphism の合成を保存する category C における morphism f g の合成を考える。 合成された $ f \circ g $ を F により mapping することと、f と g を個別に F により mapping した後に合成した結果は同じである。 \begin{equation} F(f \circ g) = F(f) \circ F(g) \end{equation} \end{itemize} functor の例を挙げる。 まず、 3つの object A,B,C と morphism f,g,h を持つ category C を考える。 category C の morphism は式\ref{exp:functor_categoryC}であるとする。 \begin{eqnarray} \label{exp:functor_categoryC} f : A \rightarrow C \\ \nonumber g : B \rightarrow B \\ \nonumber h : B \rightarrow C \\ \nonumber f = g \circ h \end{eqnarray} 次に、 2 つの object A', B' と morphism f', g' を持つ category D を考える。 category D の morphism は式\ref{exp:functor_categoryD}であるとする。 \begin{eqnarray} \label{exp:functor_categoryD} f : B' \rightarrow A' \\ \nonumber g : A' \rightarrow B' \\ \nonumber \end{eqnarray} C から D への functor F は式\ref{exp:functorF}のようなものがある。 要素数が異なるため一見正しく mapping できないように思えるが、正しく Functor 則を満たしている。 \begin{eqnarray} \label{exp:functorF} F(A) = A' \\ \nonumber F(B) = B' \\ \nonumber F(C) = A' \\ \nonumber F(id_A) = F(id_{A'}) \\ \nonumber F(id_B) = F(id_{B'}) \\ \nonumber F(id_C) = F(id_{A'}) \\ \nonumber F(f) = id_{A'} \\ \nonumber F(g) = g' \\ \nonumber F(h) = f' \\ \nonumber F(h \circ g) = F(h) \circ F(g) = f' \circ g' = id_{A'} = F(f) \end{eqnarray} functor F を用いて category C を category D へと mapping した図が図\ref{fig:functor}である。 \begin{figure}[htbp] \begin{center} \includegraphics[scale=0.8]{fig/functor.pdf} \caption{Functor の例} \label{fig:functor} \end{center} \end{figure} functor により category から category への mapping を考えることが可能となった。 % }}} % {{{ Natural Transformation \section{Natural Transformation} \label{section:natural_transformation} \ref{section:functor}節では category から category への mapping である functor について述べた。 \ref{section:natural_transformation}節では functor と functor の関係である Natural Transformation(自然変換)について述べる。 category C から D への functor F, G が存在する時、F から G への変換 t を考える。 t は category C の A に対し、F(A) から G(A) への変換を提供する(式\ref{exp:trans})。 \begin{equation} t(A) : F(A) \rightarrow G(A) \label{exp:trans} \end{equation} t が 式\ref{exp:natural_transformation}を満たす時、t を natural transformation を呼ぶ。 \begin{eqnarray} \label{exp:natural_transformation} f : A \rightarrow B \\ \nonumber G(f)t(A) = t(B)F(f) \end{eqnarray} t が natural transformation である時、 category A における A を functor F で移したのちに morphism を適用しても、 functor G で移した後に morphism を適用しても良いという可換性が得られる。 式\ref{exp:natural_transformation}の可換図は図\ref{fig:natural_transformation}となる。 \begin{figure}[htbp] \begin{center} \includegraphics[scale=1.0]{fig/natural_transformation.pdf} \caption{自然変換の可換図(式\ref{exp:natural_transformation})} \label{fig:natural_transformation} \end{center} \end{figure} % }}} % {{{ Monad \section{Monad} \label{section:monad} \ref{section:natural_transformation}節では functor 間の可換性 natural transformation について述べた。 category, functor, natural transformation を用いて Monad を定義する。 category C における monad とは以下の性質を持つ $ triple (T, \eta, \mu) $ である。 \begin{itemize} \item $ T : C \rightarrow C $ category C から C への functor T \item $ \eta : id_C \rightarrow T $ C から T への natural transformation $ \eta $ \item $ \mu : T^2 \rightarrow T $ $ T^2 $ から $ T $ への natural transformation $ \mu $ なお、 $ T^2 $ とは $ TT $ と同義であるとする。つまり functor T による mapping を2回行なうものである。 回数を n 回とすることで $ T^n $ と記述する。 \end{itemize} この $ triple (T, \eta, \mu) $ が図\ref{fig:monad_laws}の可換図を満たす。 なお、Aに対する $ \mu $ を $ \mu_A $ と記述する。 同じように A に対する $ \eta $ を $ \eta_A $ と記述する \begin{figure}[htbp] \begin{center} \includegraphics[scale=0.9]{fig/monad_laws.pdf} \caption{$ triple (T, \eta, \mu) $ が Monad であるために満たすべき可換図} \label{fig:monad_laws} \end{center} \end{figure} 図\ref{fig:monad_laws} で示した可換図は monad が満たすべき Monad則であり、T に対する演算の可換性を強制する。 $ \eta $ は T を1つ増やすオペレーションであり、$ \mu $ は T を 2 つから1つに減らすオペレーションである。 左側の可換図は T が 3つある場合の可換性である。 TTT に対して、 (TT)T のよう外側の2つのTに $ \mu $ を行なってから結果の TT に対して $ \mu $ を行なっても、 T(TT) のように内側の2つのTに $ \mu $ を行なってから結果の TT に対して $ \mu $ を行なっても可換であることを示す。 つまり、TTTを減らす演算は左から行なっても右から行なっても良いことを示す。 そして右側の可換図は T が1つである場合の可換性である。 T A に対して $ \eta $ を行ない TT A としてから $ \mu $ を行なっても、T A の内部の A に対して $ \eta $ を行ない TT A としてか $ \mu $ を行なっても、 T A の id と同じであることを示している。 つまり、Tに対する演算における左右の単位元となるような性質を $ triple (T, \eta, \mu) $ に強制することとなる。 % }}} % {{{ Category in Functional Programming \section{Category in Functional Programming} \label{section:category_in_program} \ref{section:monad}節では Monad の定義について述べた。 これからプログラムにおける Monad について述べていく。 そのために\ref{section:category_in_program}節はプログラムと category の対応について述べる。 プログラムには値と関数のみが存在するとする。 任意の値は型付けられるとする。 変数x が型 A を持つ時、式\ref{exp:value_in_program}のように記述する。 \begin{equation} \label{exp:value_in_program} x : A \end{equation} 関数は値を受けとり値を返すものとする。 A を取り B を返す関数f の型は式\ref{exp:function_in_program}のように記述する。 \begin{equation} \label{exp:function_in_program} f : A \rightarrow B \end{equation} そして、引数と返り値の型が等しい関数は関数結合できるとする。 関数結合の記号には $ \circ $ を用いる。 例えば、 A を取り B を返す関数 f と B を取り C を返す関数 g の合成は式\ref{exp:function_compose_in_program}のようになる。 \begin{eqnarray} \label{exp:function_compose_in_program} f : A \rightarrow B \\ \nonumber g : B \rightarrow C \\ \nonumber g \circ f : A \rightarrow C \end{eqnarray} この時、型を object とし、関数を morphism とする category が構成できる。 この category が category が満たすべき法則を満たしているか確認する。 \begin{itemize} \item 全ての object について identity mapping が存在する 任意の型 A に対し $ A \rightarrow A $ である関数 id が定義できれば identitiy mapping が存在することになる。 任意の型の値x を受けとり、その値を返す関数が id となる。 \item 同じ obejct が domain と codomain になっている2つのmorphismは合成することができ、合成の順番は結果に影響しない。 morpshim は関数である。 つまり domain は引数の型であり、 codomain は返り値の型となる。 morphism の合成は関数合成に相当し、合成の順序によらず引数と返り値の型は同じとなる。 \end{itemize} プログラムに対応する category が構成できた。 特に例として用いているプログラミング言語 Haskell では値と関数は型を持つため、 category との対応が分かりやすい。 よって例題には Haskell のプログラムを用いることとする。 % }}} % {{{ Functor in Functional Programming \section{Functor in Functional Programming} \label{section:functor_in_program} \ref{section:category_in_program}節ではプログラムとcategoryが対応していることを述べた。 \ref{section:functor_in_program}節ではプログラムにおけるfunctor について述べる。 プログラムにおけるfunctor は型引数を持つことのできるデータ型に対応する。 型引数を持つデータ型とは、任意のデータ型に対して構成可能なデータ構造であり、List などが相当する。 たとえば、List は数値の List であっても Bool の List であっても構成可能である。 この List を、型を受けとり型を返す型であると考えると、渡す型が引数のように振る舞う。 この引数が型引数である。 \begin{eqnarray} \label{exp:functor_type} A : Type \\ \nonumber List A : Type \\ \nonumber List : Type \rightarrow Type \end{eqnarray} つまり、型と関数から構成される category から List 型と List に対応する関数からなる category へと置きかえるような functor が存在すれば良い。 Haskell では functor はリスト \ref{src:functor_in_haskell} のように型クラスとして提供される。 \begin{table}[html] \lstinputlisting[label=src:functor_in_haskell, caption=Haskell における Functor の定義] {src/functor_class.hs} \end{table} functor であることを保証したい型は f として表される。 そしてデータ型が functor であることを示すためには、 fmap という関数を定義すれば良いことが分かる。 fmap は型a から型bへの関数を取り、f a を取り f b を返す。 つまり、f でない型への演算をfの型においても適用するために変換する関数である。 morpshim は関数であるため、 $ A \rightarrow B $ の morphism を $ F A \rightarrow F B $ へと mapping する役割を担っていることが分かる。 よって morphism を morphism へと mapping することができるため functor となる。 また、fmap の型に $ f a $ が存在するように、 f は型を引数として受けとっている。 ここで object は型であるため、 $ A $ の object を $ F(A) $ への mapping する役割をf が担っていることが分かる。 よって型引数を持つ型f と対応する fmap を定義することにより functor が定義できる。 なお、 fmap の型を \verb/ fmap :: (a -> b) -> ((f a) -> (f b))/ と読むことで、関数を受けとりfにおける関数に変換していることが分かりやすくなる。 functor の例として、型がInt である変数 x と Int から Bool を返す even 関数を考える。 このプログラムがなす category C は object が Int であり、 morphism が show となる。 category C を functor によって別の category に写すことができる。 例えば List がなす category がある。 まずHaskell において List を定義する。 List は任意の型 a を取り、 List a とする。 空の List は Nil とし、List a に対して a の値を Cons で追加することによって List を構築するとする。 ここで List が Functor であると定義する。 fmap は a を取りbを返す関数を取り、List a を取って List b を返す関数である。 つまり、関数を取ってList の全ての要素に適用することで実現できる。 定義した結果がリスト\ref{src:list_in_haskell} である。 \begin{table}[html] \lstinputlisting[label=src:list_in_haskell, caption=Haskell におけるListの例] {src/list.hs} \end{table} Int型を持つ値x と、Intから Bool返す関数 even を考える。 even を x に適用すると Bool となる。 この際、x を持つ List の型は List Int であり、 fmap によって even を List Int に適用すると List Bool となる。 Haskell における実行結果はリスト\ref{src:exec_list_in_haskell} のようになる。 \begin{table}[html] \lstinputlisting[label=src:exec_list_in_haskell, caption=Haskell における List の実行例] {src/exec_list_in_haskell.txt} \end{table} なお、 Haskell において型Aを持つ値xは $ x :: A $ のように記述される。 x と even からなるプログラムから、型List と fmap を用いることにより List におけるプログラムでも同じように Bool が得られる。 これを通常のプログラムから List のプログラムへの functor とみなす。 このように、型引数を持つ型とfmapによる関数の変換を定義することによってプログラムにおける functor を実現する。 可換図で表現すると図\ref{fig:functor_in_haskell}となる。 \begin{figure}[htbp] \begin{center} \includegraphics[scale=0.8]{fig/functor_in_haskell.pdf} \caption{Haskell における Functor の例がなす可換図} \label{fig:functor_in_haskell} \end{center} \end{figure} functor の定義にあたり、\ref{section:functor}節で示したように Functor則を満たすようにデータ型と fmap を定義しなくてはならない。 Haskell における Functor則はリスト\ref{src:functor_laws_in_haskell}のように表される。 \begin{table}[html] \lstinputlisting[label=src:functor_laws_in_haskell, caption=Haskellにおける Functor則] {src/functor_laws_in_haskell.txt} \end{table} 1行目がid の保存に、2行目が関数の合成の保存に対応している。 なお、 Haskell における関数合成は \verb/./ によって行なわれる。 % }}} % {{{ Natural Transformation in Functional Programming \section{Natural Transformation in Functional Programming} \label{section:natural_transformation_in_program} \ref{section:functor_in_program}節ではプログラムにおける functor を定義した。 \ref{section:natural_transformation_in_program}節ではプログラムにおける natural transformation について述べる。 natural transformation は functor と functor 間の変換である。 プログラムにおいて functor は型引数を持つデータ構造であったため、データ構造からデータ構造への変換となる。 natural transformationは図\ref{fig:natural_transformation}で示したような可換図が可換になるような t であった。 つまりある functor f と g があった時に、 f において morphism を適用してtを適用しても、t を適用してから g において morphism を適用しても結果が変わらないような t を提供できれば良い。 プログラムにおける natural transformation は t は図\ref{fig:natural_transformation}を満たすような関数として表現される。 Haskell において特定の関数が持つ性質を制約として記述することはできないため、Haskell において natural transformation の具体的な定義は存在しない。 List における natural transformation の例としては、List の先頭を除いた List を返す tail 関数がある(\ref{src:natural_transformation_in_haskell})。 \begin{table}[html] \lstinputlisting[label=src:natural_transformation_in_haskell, caption=List の先頭を除いた List を返す tail 関数] {src/natural_transformation_list.hs} \end{table} 100, 200, 300 の数値を持つ List Int を考える。 この List が functor f に相当する。 Int を取り Bool を返す関数 even を fmap により適用すると List Bool が得られる。 ここで natural transformation tail を考える。 tail は List a から List a への関数であるため、 functor f,g は両方とも List である。 natural transformation の定義から、 tail を行なってから fmap even しても、 fmap even を行なってら tail を行なっても結果が同じであれば良い。 実行した結果がリスト\ref{src:exec_tail}である。 \begin{table}[html] \lstinputlisting[label=src:exec_tail, caption=tail の実行例] {src/exec_tail_in_haskell.txt} \end{table} tail (fmap even list) と fmap even (tail list) の型が同じように List Bool であり、値も等しいことが分かる。 なお、 tail は同一名の関数が既に定義されているため、 Main.tail として名前を指定している。 List の値を変換してから先頭を除いても、List の先頭を除いてから値を変換しても結果が同じであることは直感的にも正しいように思える。 これがプログラムにおける natural transformation である。 図で表すと、図\ref{fig:natural_transformation_in_program}のような可換図となり、これは可換である。 \begin{figure}[htbp] \begin{center} \includegraphics[scale=1.0]{fig/natural_transformation_in_haskell.pdf} \caption{natural transformation tail の可換図} \label{fig:natural_transformation_in_program} \end{center} \end{figure} % }}} % {{{ Monads in Functional Programming \section{Monads in Functional Programming} \label{section:monads_in_program} \ref{section:natural_transformation_in_program}節ではプログラムにおける natural transformation について述べた。 \ref{section:monads_in_program}節ではプログラムにおける Monad について述べる。 Monad とは 図\ref{fig:monad_laws}の可換図を満たす $ triple (T, \eta, \mu) $ であった。 T は functorであり、$ \eta $ は $ A \rightarrow T A $ 、 $ \mu $ は $ T^2 A \rightarrow T $ の natural transformation であった。 Haskell において functor は型引数を持つ型とfmapで表現され、 natural transformation は図\ref{fig:natural_transformation}の可換図を満たす関数であった。 つまり、型と fmap、2つの関数を適切に定義することによって表現される。 Haskell において、 $ \eta $ と $ \mu $ の型は以下のようになる。 \begin{itemize} \item eta : \verb/A -> T A/ ここで、 T は functor である型引数を持つ型 \item mu : \verb/T (T A) -> T A/ 本来の $ \mu $ は $ T^2 \rightarrow T $ であるため、 T によって2度 mapping された値から T によって mapping された値への関数になる。 \end{itemize} Monad 則により、Tに対する演算は単位元のように振る舞わせることと、演算の可換性を持つ。 ここでメタ計算を T に対する演算として定義する。 そうすることで、型Aを持つ値に対する計算から functor Tにより T の計算へと変換し、メタ計算部分は T に対する演算として行なうことで任意のAに対応するメタ計算を行なうことができるようになる。 これがプログラムにおける monad を通したデータ型とメタ計算の対応である。 そして、 Monad 則はメタ計算をTに対して $ \eta $ と $ \mu $ で定義する際に満たすべき制約として機能する。 Haskell において List は monad としても振る舞う。 List は nondeterminism (非決定性)を表現するデータ構造である。 List に対する演算を行なう際、結果としてありうる値を全て列挙することにより非決定性を表現しようとするものである。 List において非決定性を表現する時、$ \eta $ は通常の値から List を構築する関数、 $ \mu $ は List の List から List を返す concat 関数となる。 $ \mu $ で何故非決定性を表現できるかと述べる。 まず、List a と、 a から List a を返す関数を考える。 List a がありうる値の集合で、関数が値から取りうる値を計算して返すものだとする。 List a に対して fmap することで、ありうる値に対して全ての取りうる値を計算することができる。 この際、 List a に対して a から List a を返す関数を適用するために List a を持つ List が構築される。 ネストされた List を、全ての取りうる値として通常の List に戻すために concat を用いる。 ここで、Haskell における monad の型クラスを振り返る(リスト\ref{src:monad_class})。 Haskell においては monad は Monad 型クラスとして提供されているが、$ \eta $ と $ \mu $ による記述はされていない。 これは、Haskell がメタ計算との対応として Kleisli Category の triple を採用しているからである。 monad と Kleisli category は互いに同型であるために相互に変換することができる。 また、 category や program の文脈が変わることで $ \eta $ を unit と呼んだり、 $ \mu $ を join と呼んだり、 \verb/>>=/ を bind と呼んだりする。 しかし今までの解説には $ \eta $ と $ \mu $ を用いているために、このまま $ \eta $ と $ \mu $ で解説していくこととする。 なお、monad と Kleisli triple との変換は Haskell においてはリスト \ref{src:monad_and_bind} のようになる。 \begin{table}[html] \lstinputlisting[label=src:monad_and_bind, caption=Haskell における monad と Kleisli triple との変換] {src/monad_and_bind.hs} \end{table} では List を用いて非決定性を表現した例を示す。 まずは List を monad とする(リスト\ref{src:list_monad})。 \begin{table}[html] \lstinputlisting[label=src:list_monad, caption= Haskell における List に対する monad の定義] {src/list_monad.hs} \end{table} 先程述べたように、 $ \eta $ は値を List とする関数、 $ \mu $ はList を内包する List を全て結合する関数である。 この List Monad を実行する。 まずはありうる値のリストとして100, 200, 400 を持つ List の x を考える。 次に、値から取りうる計算結果を返す関数として f を定義する。 取りうる計算結果として、1加算した結果、10加算した結果、2乗した結果が存在するとし、結果は List として返す。 この x と f から全ての取りうる値を計算した結果がリスト\ref{src:exec_list_monad}である。 \begin{table}[html] \lstinputlisting[label=src:exec_list_monad, caption= Haskell において List を monad として実行した例] {src/exec_list_monad.txt} \end{table} 3つのありうる値に対して、取りうる3つの計算結果から生成された9つの値が全て計算されている。 このように、ある性質を持つデータ型と、そのデータ型を返す関数の演算によって通常の計算に加えてメタ計算を実現することができた。 これがプログラムにおける Monad であり、結果としてメタ計算とデータ型の対応が得られる。 なお、 Haskell においても Monad 則は存在し、リスト\ref{src:monad_laws_in_haskell}の性質を満たすよう $ \eta $ や $ \mu $ を定義しなくてはならない。 \begin{table}[html] \lstinputlisting[label=src:monad_laws_in_haskell, caption=Haskell における Monad 則] {src/monad_laws_in_haskell.hs} \end{table} 1つめの則が図\ref{fig:monad_laws}における左側の可換図に対応し、Tに対する演算の可換性を示す。 2つめの則が図\ref{fig:monad_laws}における右側の可換図に対応し、Tに対する演算における単位元のような振舞いを強制する。 3つめの則が eta に対する natural transformation の要請であり、4つめの則が mu に対する natural transformation の要請である。 % }}}