bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

表現可能関手

5. 表現可能関手

日本の歴史を学んでいるといくつもの学説に出会う。それらの中で、圏論との関わりが面白いと思った学説が、中世史にあったので、それを最初に紹介しよう。

一つは、「権門体制論」である。黒田俊雄氏の学説で、中世の国家体制は、公家権門(執政)、宗教権門(護持)、武家権門(守護)の三つの権威から成り立っていて、それらは相互補完的な関係にあると主張されているものである。

また、佐藤進一氏は、室町時代初期の統治体制を「将軍権力の二元論」だと言っている。これは将軍権力が主従的支配権と統治的支配権に分割され、運用されているという学説で、主従的支配権はいわゆる「御恩と奉公」を基盤とした軍事、統治的支配権は「建武式目」をベースとした法による統治である。

二つの学説に対しては賛否両論があるようだが、歴史認識から離れて、数学の圏論という立場からは、構成要素を重視している「権門体制論」は「対象」という視点から、支配者と被支配者との関係に重みを置いている「将軍権力の二元論」は「射」から中世史を考察しているように見える。物事を分析するとき、対象を中心にしてみる見方と射を中心にしてみる見方の二つがあることは面白いことだと思う。

さらに、圏論に深くかかわっていると、圏の構造を表現している射の方が大切に見える。この立場から言うと、黒田氏よりも佐藤氏の見方に同調できる部分が多いのだが、どうだろうか。

そのような議論を打ち消してくれるのが、今回の表現可能関手(representable functor)だ。対象と射の間は行ったり来たりできますよと説明してくれる。

今回の話を分かりやすくするために、別の話題を挙げておこう。今日では、電卓を使えば、三角関数や常用対数の値を簡単に求めることができるが、このような道具がなかった時代には、数表が便利な道具であった(下図)。アマゾンで調べてみると、1981年には、まだ、吉田洋一・吉田正夫著『数表(新数学シリーズ)』が出版されている。おそらく、このころからそんなに離れていない時代に、書籍としては出版されなくなったのであろう。しかし、電卓が発明されていない時代には、重宝した。
f:id:bitterharvest:20180216123738j:plain

この数表が表現可能関手だ。数表は関数をテーブルという形式で表してくれる。関数の方はもちろん圏論での射である。テーブルはデータだ。圏論での対象に当たる。従って、数表は、射を対象で表したものだとは思えないだろうか。表現可能関手は、このような関係を圏論の言葉で厳密に定義したものである。そう思うと、なあんだということになるけれども、「数表」を数学的な概念に変えた意義は大きいと思う。

5.1 自然変換

圏論の中で、自然変換は大切な概念である。関手から関手への関数が自然変換である。関手は、ある圏の構造を別の圏に、その構造を維持したまま、移動させてくれる。そして、圏を対象とし、関手を射とすることで、抽象化された新たな圏を構成できる。これは「圏の圏」と呼ばれる。そして、二つの圏の間で関手を考えたように、二つの「圏の圏」の間で、関手、この場合には自然変換と呼ばれるが、を結ぶことで、さらに抽象化が可能となる。そして、素晴らしいことに、自然変換によっても、元々の圏が有していた構造は維持される。

従って、圏論では、関手、自然変換によって、抽象化を進めることができるが、その抽象化は、もともと持っていた圏の構造を維持するという貴重な性質を有している。

そこで、ここでは、もう一度、自然変換について復習しておこう(下図)。
f:id:bitterharvest:20180215212310p:plain

今、圏\(\mathcal{C}\)が存在したとしよう。\(\mathcal{C}\)を圏\(\mathcal{D}\)に関手\(F,G\)で写したとしよう。一般に、関手によって写された空間はシート(sheet)と呼ばれる。図では、\(F\)によって写された部分を\(Sheet \ A\)とし、\(G\)による部分を\(Sheet \ B\)とした。玉ねぎは鱗片葉と呼ばれる厚肉の皮が層をなしているが、玉ねぎを圏と考え、鱗片葉をシートとすれば、イメージしやすいと思う。

自然変換はシート間で写像を結んだ時、それらが、可換になるというものだ。これだけでは曖昧なのでもう少し厳密に定義することとしよう。今、\(\mathcal{C}\)の任意の対象を\(X\)としよう。そして、\(X\)をソースとしている任意の射を\(f\)としよう。そして、そのターゲットを\(Y\)としよう。即ち、\(f : X \rightarrow Y\)としよう。ここで、\(X,f\)は任意であることに注意してほしい。

自然変換は、要素ごとに可換になっていることである。そこで、\(X\)について考える。この時、\(Sheet \ B\)から\(Sheet \ A\)への射\(\alpha\)を考えることとする。これが、自然変換となるためには、対象\(X\)によって作られた四角形、この場合には、\(F(X),F(Y), G(X),G(Y)\)が可換でないといけない、即ち、\(F(f) \circ \alpha_X\)と\(\alpha_Y \circ G(f) \)が同型でないといけない。

即ち、\(F\)と\(G\)の間に、自然変換が存在するための必要十分条件は、任意の\(X,f:X \rightarrow Y\)に対して
\begin{eqnarray}
F(f) \circ \alpha_X \simeq \alpha_Y \circ G(f)
\end{eqnarray}
が成り立つことである。なお、\(\simeq\)は同型を示す。

なお、\(Sheet \ A\)から\(Sheet \ B\)への射\(\beta\)についても同様である。これは、\(X\)が任意であることから言う必要がないが、念のためである。さらに、このことから、
\begin{eqnarray}
\alpha \circ \beta = id \\
\beta \circ \alpha = id
\end{eqnarray}
となる。これは、自然変換が存在するための必要十分な条件になっていることにも注意してほしい。

5.2 \(\bf{Hom}\)関手

それでは、対象を射として扱えるようにしよう。それは、次のように行う。対象を一つ定める。そして、この対象から出ている射を利用して、それにつながる対象を覗けるようにするというのが趣旨だ。具体的には、対象の一つを\(A\)としよう。そして、任意の対象を\(X\)としよう。これらの対象間の射の集合を\(\mathcal{C}[A,X]\)とする。ここで、\(\mathcal{C}\)はこれら対象が属している圏である。射の集合は\(\bf{Hom}\)集合ともいわれる。以後では、この言葉を用いることとする。

上の説明で、\(X\)を任意と説明した。このように任意である時は、\(\bf{Hom}\)集合は\(\mathcal{C}[A,-]\)と書く。

圏\(\mathcal{C}\)のそれぞれの対象に対して、その対象からの\(\bf{Hom}\)集合への射は、下図に示すように関手となる。この関手は\(\bf{Hom}\)関手と呼ばれる。
f:id:bitterharvest:20180215212338p:plain

上記の\(\bf{Hom}\)関手の説明では、\(A\)から出ていく射の集合を考えたが、もちろん、下図のように\(A\)へ入ってくる射の集合も考えることができる。前者は共変関手、後者は反変関手と呼ばれる。双対の関係にあるが、ここでは、共変関手を用いて、表現可能関手を説明する。反変関手の場合はどのようになるかは、別途、考えて欲しい。
f:id:bitterharvest:20180218092425p:plain

5.3 表現可能関手

今、圏\(\mathcal{C}\)から、\(\bf{Hom}\)関手を用いて、圏\(\mathcal{D}\)に写した。しかし、最初に説明したように、別の関手を用いることで圏\(\mathcal{D}\)に写すこともできる。先に説明したように、別々のシートの上に写されるのだが、これらが同型な場合もある。このとき、これらの間では自然変換が成り立つ。しかも、いくつも同型のものがある場合さえある。その中には、表現可能関手と呼ばれるものがある。これは、関手が共変である時は射のターゲット(反変の時はソース)を写すものである。この関手を\(F\)としよう。

そうすると、任意の\(X\)について下図のようになる。このとき、\(\mathcal{C}[A,X]\)と\(F(X)\)とは同型である。
f:id:bitterharvest:20180216175820p:plain
これは、関数と関数値(の列)が一対一に対応していることを意味している。

さらに、\(X\)からの任意の射を\(f\)とし、\(f:X \rightarrow Y\)とした時、下図のように可換図を得ることができる。
f:id:bitterharvest:20180216175846p:plain

上の図から次のことが言える。任意の\(X\)に対して、\(\mathcal{C}[A,X] \rightarrow F(X)\)であることから、
\begin{eqnarray}
\alpha : (A \rightarrow X) \rightarrow F(X)
\end{eqnarray}
逆に、\( F(X) \rightarrow \mathcal{C}[A,X]\)であることから、
\begin{eqnarray}
\beta : F(X) \rightarrow A \rightarrow X
\end{eqnarray}
と言える。なお、上の式では\((A \rightarrow X)\)とせず、カーリー化して\(A \rightarrow X\)とした。

そして、自然変換であるためには、次の条件を満たす必要がある。
\begin{eqnarray}
\alpha \circ \beta = id \\
\beta \circ \alpha = id
\end{eqnarray}

冒頭で、表現可能関手は数表だといった。例えば、角度に対してその正弦を与える関数\(sin’\)の数表は次のようになる。
f:id:bitterharvest:20180215212928p:plain

上の図でから、\(\alpha\)は次のようになっていることが分かる。
\begin{eqnarray}
\alpha : (Integer \rightarrow Double) \rightarrow [Double]
\end{eqnarray}
また、\(\beta\)は次のようになる。
\begin{eqnarray}
\beta : [Double] \rightarrow Integer \rightarrow X
\end{eqnarray}

5.4 Haskellで表現可能関手を使う

これらの知見を得て、表現可能関手をHaskellで実現しよう。まず、表現可能関手は、複数の異なるデータが有する共通的な性質なので、クラスとして定義しよう。ここでは、\(Representable\)と呼ばれるクラスを用意しよう。表現可能関手は今までの説明では\(F\)を用いてきたので、ここでは小文字にして次のように定義しよう。

class Representable f where

次に、このクラスで用いられる演算を定義する必要がある。それは次のものである。
\begin{eqnarray}
\alpha & :& (A \rightarrow X) \rightarrow F(X) \\
\beta &:& F(X) \rightarrow A \rightarrow X
\end{eqnarray}

しかし、この演算では、\(A\)という対象を必要としている。このため、これをデータ型として定義する必要がある。Haskellでは対象はデータ型として定義される。データ型は\(type\)を用いて定義できる。
そこで、\(A\)を型コンストラクタとし、その型は表現可能関手\(F\)、但しプログラムでは小文字の\(f\)、を用いて

class Representable f where
  type A f :: *

とすればよい。ここで、\(*\)は単にタイプであることを意味する。しかし、これでは、\(A\)の意味がはっきりしないので、表現可能であるということをはっきりさせるために、\(A\)の代わりに\(Rep\)を用いて次のように定義しよう。

class Representable f where
  type Rep f :: *

次に\(\alpha\)の機能は表を作ることなので、その意味を持たせて\(\alpha\)を\(tabulate\)とし、\(\beta\)は表から索引してくると解釈できるので、\(\beta\)を\(index\)として、関数を実現すると次のようになる。

なお、{-# LANGUAGE TypeFamilies #-}が必要である。

{-# LANGUAGE TypeFamilies #-}
class Representable f where
  type Rep f :: *
  tabulate :: (Rep f -> x) -> f x
  index :: f x -> Rep f -> x

圏論では、自然変換となるためには、
\begin{eqnarray}
\alpha \circ \beta = id \\
\beta \circ \alpha = id
\end{eqnarray}
を満たすことが必要であった。しかし、Haskellはデータ型を変数として利用するため、上の条件は常に満たされる。従って、自然変換の条件が満たされているかどうかに関心を払う必要はないという便利な面をHaskellは有している。

\(tabulate\)と\(index\)の意味については上述したが、重要なので、もう一度説明をし直しておこう。上のプログラムで、\(tabulate\)は表を作る関数である。これに対して、\(index\)は次のように理解できる。\(f \ x\)で表を得る。\(Rep \ f \)で表を索引するためのキーを与える。このため、しばしば、\(Rep\)に変わって\(Key\)という名前を使っている例も見かける。そして、このキーで索引して得られるのが値\(x\)である。

それでは、このクラスを利用してみよう。表現可能関手として、リストを用いてみよう。ただし、このリストは空リストを含まないものとする。そこで、次のデータ型\(Stream\)を用意しよう。

data Stream a = Cons a (Stream a)

これを用いて、クラス\(Representable\)のインスタンスを用意しよう。次のようになる。

instance Representable Stream where
  type Rep Stream = Integer
  tabulate f = Cons (f 0) (tabulate (f . (+1)))
  index (Cons x xs) n = if n== 0 then x
                                 else index xs (n-1)

\(Rep \ Stream\)のデータ型は\(Integer\)としよう。ただし、0より大きい整数とする。上記で少し述べたが、ここでは、\(Rep\)よりは\(Key\)のほうが分かりやすいと思う。リスト(\(Stream\))への索引に用いているキー(\(Key\))のデータ型は\(Integer\)であると考えたほうが素直である。

次に関数の部分を説明しよう。\(tabulate\)は\(Stream\)、即ち、表(リスト)を作成する。表の中に入るのは、\(f : A \rightarrow X\)とした時、\(f(A)\)である。そこで、\(f\)を引数にして、表を作成できるようにする。即ち、\(tabulate\)は、関数\(f\)に対して、\([ f(0), f(1),f(2),…]\)の表を作成する。

\(index\)は、表の\(n\)番目に入っている値を取り出す。

それでは、この表現可能関手を用いてみよう。

最初の例は、\(y= 2 \times x\)の数表を求めるものである。

a = tabulate (*2) :: Stream Integer

この数表は、\(n\)番目の場所に\(2 \times n\)の値が入っている。それでは、5番目のところを求めて見よう。

Prelude> :load "Representable.hs"
[1 of 1] Compiling Main             ( Representable.hs, interpreted )
Ok, modules loaded: Main.
*Main> a = tabulate (*2) :: Stream Integer
*Main> index a 5
10

それでは、\(n\)番目の場所に\(a\)という文字が\(n\)個は言っている数表の例を考えてみよう。数表は次のようになる。

*Main> toLetter n = if n==0 then "" else "a" ++ toLetter (n-1); b = tabulate toLetter :: Stream String

やはり、5番目の値を求めて見よう。

*Main> index b 5
"aaaaa"

最後に、角度からその正弦を求める関数\(sin'\)の数表を考えよう。\(n\)番目には、\(n\)度に対する制限が入っていることにする。数表は次のようになる。

*Main> sin' :: Integer -> Double; sin' x = sin $ (fromIntegral x) * pi / 180; c = tabulate sin' :: Stream Double

それでは5度の時の正弦を求めて見よう。

*Main> index c 5
8.715574274765817e-2

上記のプログラムを図で示すと下図のようになる。
f:id:bitterharvest:20180216091155p:plain

プログラムの全体を示しておこう。

{-# LANGUAGE TypeFamilies #-}

class Representable f where
  type Rep f :: *
  tabulate :: (Rep f -> x) -> f x
  index :: f x -> Rep f -> x 

data Stream a = Cons a (Stream a)

instance Representable Stream where
  type Rep Stream = Integer
  tabulate f = Cons (f 0) (tabulate (f . (+1)))
  index (Cons x xs) n = if n== 0 then x
                                 else index xs (n-1)

a = tabulate (*2) :: Stream Integer
--index a 5

toLetter n = if n==0 then "" else "a" ++ toLetter (n-1)
b = tabulate toLetter :: Stream String
--index b 5

sin' :: Integer -> Double
sin' x = sin $ (fromIntegral x) * pi / 180
c = tabulate sin' :: Stream Double
--index c 5

成長させ過ぎた脂肪腫を除去する

お医者さんに「成長させ過ぎですね」と言われた背中のこぶを取ることになった。あの事件が起きなければ、いまだに、こぶが自己顕示欲を高めながら、息づいていたかと思うと、なくなってしまった今となっては哀れにさえ思える。

事件は突然起こった。昨年の秋は雨の多い日が続いたが、そのようなある日、冷蔵庫の中が寂しくなってきたので、近くのスーパーに買い物に出かけた。野菜や果物や肉を満載したカートを押しながら屋上の駐車場に出ようとしたときだった。運の悪いことに、駐車場への出口はスロープになっていて、表面は雨で濡れていた。

スロープに2,3歩踏み出したとき、両足のテニス靴がゆっくりと滑り始めた。体を支えようとしてカートに体重を移した瞬間に、カートが勢いよく逃げていった。あっという間の出来事なのだが、転んでいくなか、スローモーションのようにコントロールのできない体の動きを体験する。テニス靴がスピードにのって前にどんどん滑っていく。腰が後方に取り残され、お尻がまっすぐにすとんと落ちていく。一生懸命に体を支えようとした左の肘を、したたかにコンクリートに打ち付けつけてしまった。

後ろにいたおじさんに、「大丈夫ですか」と心配される。強がって、「大丈夫です」といったものの、かなり強く打ったようで、痛みは激しい。手を動かすことはできたので、たいしたことではないだろうと高をくくっていた。

このような事件があったことも忘れていた2か月後に、湯船につかって、肘をお風呂の側面に押し付けたとき、感覚がないことに気が付いた。触ってみると、肘に大きなこぶができている。柔らかいこぶで、手で押すと移動する。転んだことが原因なのだろうかと思い、定期検査の時に知り合いの医者に相談したら、整形外科で見てもらってはというアドバイスを受けた。

毎年、近くの病院でインフルエンザの予防接種を受けているが、都合のよいことに、その病院のお嫁さんが、独立して新たに整形外科の病院を開院したので、早速、そこで、診察を受けることにした。開業二日目で、ほかに患者もおらず、丁寧に時間をかけて診察していただいた。肘のこぶを見た瞬間、看護婦さんと顔を見合わせて、嬉しそうに「滑液包炎ね」と言われた。通常は、滑液包の中に少量の液体が入っていて、クッションの役割をしているのだが、転んだことで炎症を起こし、滑液が余分にたまっているということで、注射針を刺して抜いてもらった。

そして、もう一つこぶがあるので診てくださいとお願いしたところ、「二つもこぶのある人は珍しい」と言いながら、背中に目をやった瞬間、「こちらの方は大変」と言われた。5cmを超えると「生検」が必要とも言われた。とりあえず、エコーで調べてみようということになったが、大きすぎて全体が写らないとのこと。「ずいぶん大きい」と言われた。大きな病院で、「MRIを撮ってもらい、それで判断しよう」ということになった。

紹介された病院でMRI検査を受けた。MRI検査の技師の人からも「大きいですね」と言われる。そして、MRI装置のベッドにうつ伏せに寝てくださいと指示される。顔の部分にタオルを敷いてもらい、顔を横向けにする。すごい音がするということで、耳栓をする。撮影には30分程度かかったが、顔を横に向けていたため首が痛くなり、往生した。技師の人によると、「おそらく脂肪腫でしょう」ということだった。ただ、通常は、一つの袋の中に脂肪が収まっているが、いくつもの袋があるようだといって、画像を見せてくれた。綺麗に撮れるものだなあと感心しながら、CDに画像をコピーしてもらった。

整形外科医に戻って、MRIの検査結果を診断してもらった。脂肪腫ということだった。脂肪腫は、よくみられる病気の一つのようで、妻の知り合いの中には手術を受けた人が少なからずいた。文字通り、脂肪の塊が体の中にできるのだが、その原因は不明だそうだ。5cm以下の大きさなら様子を見ることになるそうだが、残念ながらその大きさをはるかに超えて、直径9㎝、厚さ3㎝であった。手術をし、さらに、生検した方がよいとのことだった。この病院では手術はできないので、大きな病院を紹介するということなので、お願いした。

年が明けて、紹介してもらった病院を訪れた。ここのお医者さんにも、見た瞬間に、「大きいですね。成長させ過ぎましたね」と言われた。そして、「ここまで大きいと、麻酔も多く使うし、大きな空間ができるので溜まった血液を抜くためのドレーンも必要になります」と言われた。手術は、1時間程度、日帰りで大丈夫ですということなので、お願いした。

手術前の検査が必要ということで、尿検査、血液検査、レントゲン検査を受けた。医療者の安全を守るために、HVI検査、肝炎検査が含まれていた。

1週間後の手術ということだったが、運悪く、風邪をひいてしまった。予定していた手術の前日に病院に相談したら、「風邪が完全に治ってから改めて手術日を決めましょう」ということになった。

再度仕切り直して、1月30日に手術することになった。手術の当日、1階の処置室で手術着に着替え、看護婦さんに案内されて、2階の手術室に向かう。手術には、診察してくれた主治医の先生のほかに、若いお医者さんが一人加わっている。看護婦さんは2人だ。

MRI検査での嫌な記憶が残っていたため、うつ伏せに寝ることに抵抗があったが、今回は、苦しくならないように配慮されていた。胸のところに小さなウォータベッドのようなものが置いてあり、上半身を支えられるようになっている。これで、頭を横に向けなくても済むと思って安心する。顔が当たる部分には、タオルが置かれていたが、タオルを追加してもらい、顔が快適な位置にくるように調節する。

ベッドに横たわると、左の血管には点滴用の針が刺され、指には脈拍数・酸素測定のための、右の腕には血圧計のための器具が装着される。また、左の太ももに電気メスの対極板が貼られた。

背中の部分だけを出して、他の部分は大きなシーツで覆われる。このため、シーツの中に顔が埋まってしまい、周りは全く見えない。看護婦さんが、シーツをめくり、声をかけてくれるときだけ、孤独感から解放される。

準備の間、二人のお医者さんが相談している。「これは大きい」と言っている。こういう時のお医者さんの心境はどのようなものなのだろう。複雑な手術で嫌だなあと感じているのだろうか、それとも、普段できないことに挑戦できるのでうれしいと思っているのだろうか。私なら絶対に楽しいと思うのだが、お医者さんもそうであってくれればと願っているうちに、「タイムアウトです。それでは始めましょう」と言う。手術の時の決まりなのだろう、「大きい脂肪腫の除去。氏名はXXさん。年齢はX」と言っていよいよ始まる。

局部麻酔なので、背中の数か所に麻酔が注射される。針が入っていくときの痛みはあるが、たいしたことはない。手術を受けるにあたって、MRIの画像をパソコンであらかじめ詳細に見て、素人なりに手術の手順を想定してきたので、お医者さんの会話や背中からの刺激をもとに、進行状況を想像する。

最初は、皮膚の部分を切っているのだろう。少し、焦げたような臭いがする。手術の前に、「痛みを感じたら言ってください。麻酔を足しますから。我慢して、急に飛び上がられても危険ですから、お願いします」と言われていた。切り始めて、すぐに痛みを感じ始めたので、「痛いです」と言うと、麻酔を足しますといって処置してくれた。

脂肪腫の取り出しが始まったのだろう。右上の方を切断しているようだ。何かで引っ張っているのだろうか抵抗感がある。そうこうするうちに、電気メスで切り始めたのだろうか、痛みを感じる。この痛み、何とも不思議だ。今までは感じたことがないような痛みだ。それほど強いわけではない。何か、びりびりとくるような気持のよくない痛みだ。我慢できそうなのだが、手術前に言われているので、痛いことを何度となく告げる。

この部分が終了し、他の部分を処置した後、下側の切除が始まる。同じような痛みを感じる。同じ痛さなのだが、痛みは長いこと継続していると、耐えることが苦痛になってくる。「痛い」と連発する。麻酔を足しても、痛いというので、若いお医者さんが疑問に思ったようだ。「どこら辺が痛いですか」と質問してくる。「背中の下の方です」と答えると、主治医の先生が、「複雑な部分を切除しています。全部、取りきらないと再発する恐れがあります。もう少しで終了しますので、頑張ってください」という。難しい部分だったのだろう、若いお医者さんが「先生、お見事ですね」と言う。うまくいったのだろう。主治医の先生が、「気をつけないとこの部分は穴をあけてしまうよ」と言っている。主治医の先生はきっと上手な外科医なのだろうと安心する。

他の部分の切除が始まる。軽やかに切れていくような感触が伝わってくる。安心して、静かにしていると、お医者さんの方が心配になったのだろう。「XXさん、大丈夫ですか」と確認に来る。「大丈夫です」と元気に応えて、お医者さんに不要な心配をかけないようにする。でも、緊張が続いたせいか、右側のこめかみの血管のあたりで頭痛を感じるようになる。血圧が高くなっているためかなと思って、気を静めるように心がける。あまりに静かにしていたためか、お医者さんから再度確認が入る。同じように、元気に「大丈夫です」と答える。その後も何回かは痛い時があったので、時々は痛いですと訴えた。そうこうするうちに、「もう少しで終わりです。止めてくれと言われても、もうダメです。もう少し頑張りましょう」と言われた後は、静かに終了するのを待った。

看護婦さんがシーツをあげて、「切り口をきれいに縫っていただいて、終わりになります」と教えてくれた。やっと、終わりかとほっとする。お医者さんの方は、「きれいにと注文されたので、心して綺麗に縫わなければ」と冗談を飛ばしながら、作業をしてくれた。その間、縫合に使われる糸の特徴についてお医者さんの間で意見を交わしていた。ゆるみやすい糸があるなどと言っていたが、今回は、どのような糸を選んでくれたのだろうか。

最後に、脂肪腫除去後の空間に溜まる血液を排出するためのドレーンがつけられ、手術は無事、終了した。

看護婦さんの話では、電気メスによる痛みは広い範囲に及ぶので、麻酔の効いていない離れた部分で痛みを感じるのだろうと言っていた。「我慢すればよかったのですが、なかなかね」と看護婦さんに言うと、「我慢しなくてよかったのですよ」と言われた。麻酔は39cc使ったそうだ。あらかじめ40ccを用意したとのこと。この量が限度なのだろう。配分を考えながら、麻酔を打ったものと思われる。このため、痛いといったとき、麻酔が足されないことも多かったのではないかと推察した。

ドレーンは、切開場所ではなく少し離れたところに、差し込まれている。そこに細い管が繋がっていて、その先には、液を溜める容器が接続されている。血液はスポイトと同じ原理を用いて自然と抜かれる。液を溜める容器は弾力性があり、容器に取り付けられている栓を外し、容器を押してペチャンコにすると中の空気が抜ける。この状態で、栓をすると容器の中が減圧された状態となるので、ドレーンの管を通して手術跡に溜まった血液を容器に排出できる。簡単な装置なのだが、何とも、よくできている。ドレーンは4日後に取り外しましょうとなった。

お風呂は足浴ならぬ下半身浴ならばよいとのこと、シャワーは浴びてもよいとのことだったが、傷口が濡れるのが嫌だったので、シャワーの方はしばらく我慢することとした。2月10日に生検の結果が分かる。良い結果であることを期待している(検査結果はただの脂肪腫だった。一安心した)。

複雑な手術を手際よくこなし、順調に痛みもなく回復してゆく状況に接するとき、プロとしては当たり前のことなのだろうが、お医者さんはすごいなあと改めて感じて、感謝の気持ちでいっぱいになった。

自由モノイド

4.自由モノイド

圏論では、普遍性という概念はとても大切である。普遍性は、その言葉が示すように、数学の多くの分野で共通する性質を示したものである。前の記事で説明した極限と余極限も広い分野での共通の性質であるため、圏論での重要な普遍性の一つとなっている。ここでは、普遍性のさらなる例として自由モノイドを説明する。

4.1 自由モノイドの定義

加算や乗算などの二項演算子を用いて計算されるものはモノイドと呼ばれる。モノイドについても普遍性を考えることができる。それは自由モノイドと呼ばれるものだ。これを説明するために、モノイドについて、まずは、集合論での定理から始めてみよう。

1)集合論での定義

集合論でのモノイドは、ある集合\(M\)に対して、
1) 二項演算子\(\mu\)が存在し、\(\mu : M \times M \rightarrow M\)である。
2) 単位律が成り立つ。即ち、単位元\(e\)が存在し、任意の\(x \in M\)に対して\(e \ \mu \ x = x = x \ \mu \ e\)である。
3) 結合律が成り立つ。即ち、任意の\(x,y,z \in M\)に対して\( (x \ \mu \ y) \ \mu \ z = x \ \mu \ (y \ \mu \ z) \)である(わかりにくい時は、\(\mu\)を\(\times\)あるいは\(+\))などで置き換えるとよい。

モノイドの例を挙げておこう。整数での加算は、単位元を0とするモノイドである。ただし、どの整数も二つの整数を加算することで得られるが、その組み合わせは無数である。例えば、3という数は、0+3,1+2, 2+1,3+0,4+(-1),....となる。

もう一つの例は数である。例えば、346という数は、3という数に、4という数を接続し、さらに、6という数を接続することで得られる。これは、加算とは異なり、一つの数を得る方法は複数存在せず、一意であることに注意する必要がある。

そこで、モノイドを作る元(これを生成元と呼ぼう)を用意して、モノイドの定義に従って、集合\(M\)を得ることを考えよう。

今、生成元を\(a,b\)としよう。これに二項演算子\(\mu\)を適用して、モノイドの集合\(M\)を作ってみよう。\(M\)には、生成元と単位元が含まれる。そこで、\(M\)に\(e,a,b\)を含めよう。

次に、これらから\(M\)の新しい要素を作ろう。また、新しい要素は\(\mu\)を省いて表すことにする。
\begin{eqnarray}
a \ \mu \ a &=& a a \\
a \ \mu \ b &=& a b \\
b \ \mu \ a &=& b a \\
b \ \mu \ b &=& b b \\
a \ \mu \ e &=& a \\
e \ \mu \ a &=& a \\
b \ \mu \ e &=& b \\
e \ \mu \ b &=& b
\end{eqnarray}

ここまでで、\(M=\{e,a,b,aa,ab,ba,bb\}\)となる。
新しく得られた要素を利用して、さらに\(M\)の要素を作ってみよう。
\begin{eqnarray}
a a \ \mu \ a &=& a a a \\
a a \ \mu \ b &=& a a b \\
a b \ \mu \ a &=& a b a \\
a b \ \mu \ b &=& a b b \\
b a \ \mu \ a &=& b a a \\
b a \ \mu \ b &=& b a b \\
b b \ \mu \ a &=& b b a \\
b b \ \mu \ b &=& b b b \\
a a \ \mu \ e &=& a a \\
…………..
\end{eqnarray}

ここまでで、\(M=\{e,a,b,aa,ab,ba,bb,aaa,aab,aba,abb,baa,bab,bba,bbb\}\)をえる。
この操作を繰り返すことで、モノイドの要素を求めることができる。そして、このように定義だけを利用して作られたモノイドは自由モノイドと呼ばれる。

これに対して、出現した生成元の順番が異なるにもかかわらず、それらを同一なものと見直すモノイドもある。例えば、加算や乗算では、\(a \ \mu \ b= b \ \mu \ a\)と見なされる。このようなモノイドは自由モノイドとは言わない。

2)圏論での定義

圏論ではモノイドは、集合論での定義の時に用いた\(M,e,\mu\)を利用して、次のように定義される。圏論を学び始めると、モノイドは次のように定義される
1) 対象:シングルトン(星印で表されることが多い)
2) 射:集合\(M\)の要素
3) ソースとドメイン:*
4) 恒等射:単位元\(e\)
5) 結合:関数\(\mu\)
さらに、単位律、結合律が成り立つである。

さらに学習を続けると、モノイドは、抽象度をたためて、モノイド圏として次のように定義される。

1) 対象:関手の集まり\(\{M,I\}\)
2) 射:自然変換\(\mu\)
3) ドメイン・コドメイン
4) 恒等射:自然変換 \eta
5) 合成:\(\circ\)

ここで、モノイド圏の\(M\),\(\mu\)は、集合論での集合\(M\)と二項演算子\(\mu\)と同じ役割を持つ。また、モノイド圏の\(I\), \etaは、集合論での単位元\(e\)と同じ役割を持つ。ただし、モノイド圏では、\(M\)と\(I\)は対象となっている。そして、専門的な用語で説明すると、\(M\)は自己関手で、\(I\)は恒等関手である。射\(\mu\)は、\(M \times M\)から\(M\)への自然変換である。抽象度が上がっているので、特殊な用語を用いているが、二項演算子の定義と同じ役割を担っている。恒等射 \etaは、\(I\)を\(M\)に写す自然変換である。これは、単位元をモノイドの集合に組み込む仕掛けを与えていると思えばよい。

また、モノイド圏である時、上記で説明した関手と自然変換の間では次の可換図が成り立つ。この可換図は、\(mu:M \times M \rightarrow M\)であり、 \eta :I \rightarrow Mである。
f:id:bitterharvest:20180131205201p:plain

なお、この互換図から、モノイド圏では単位律と結合律が満たされることが分かる。これらについて詳しく知りたいときは、モノイド圏の記事を見て欲しい。

3)Haskellとの関係

Haskellでの自由モノイドは、リストである。圏論での自由モノイドと対比させると、恒等射は[ ]、結合は++となる。対象は、数を扱っている場合であれば数のリスト、文字を扱っている場合であれば文字のリストとなる。

例えば、数のリストだとすると、次のような例を得ることができる。

Prelude> a=[]
Prelude> b = [1]++[]
Prelude> b
[1]
Prelude> c = [2]++b
Prelude> c
[2,1]
Prelude> d =[2,4,5]++c
Prelude> d
[2,4,5,2,1]
Prelude> :t d
d :: Num a => [a]

文字のリストの例も挙げておこう。

Prelude> a=[]
Prelude> b= ['a']++a
Prelude> b
"a"
Prelude> c =['n','a','r']++b
Prelude> c
"nara"
Prelude> :t c
c :: [Char]

自由モノイドではないモノイドの例としては、数での加算や乗算などを挙げることができる。
乗算のモナイドを例にとってみよう。最初の例で、恒等射[ ]を1とし、結合++を*にすると、

Prelude> a=1
Prelude> b = 1*a
Prelude> b
1
Prelude> c = 3*b
Prelude> c
3
Prelude> d = 2*5*6*c
Prelude> d
180
Prelude> :t d
d :: Num a => a

4.2 集合論をベースにしたモノイドの圏

集合論の世界の中で、モノイドを構成する要素を生成元から作り出す手順を説明したが、これを圏にすることとしよう。そして、この圏を\(\mathcal{Set}\)と呼ぶこととしよう。

このために、生成元だけで構成された集合\(X\)を用意する。次に、この生成元から自由モノイドを作り出す。ここで得られた集合を\(M\)とし、\(\mathcal{Set}\)の対象の一つとすることとしよう。なお、自由モノイドは、その生成の仕方からわかるように、一つであることが分かる。なお、この時の生成方法を射\(p:X \rightarrow M\)とする。

さらに、\(\mathcal{X}\)を必要な生成元とすることで作られたモノイドの集合\(N,N’…\)をそれぞれ\(\mathcal{Set}\)の対象の一つとすることとしよう。この時の生成方法を射\(q,q’...:X \rightarrow N,N’\)とする。

このようにして作られるモノイドには、異なる二つの要素間での二項演算の結果が一致するような加算や乗算から作られたものが含まれるし、\(\mathcal{X}\)にさらに元を加えて作り出されたモノイドなども含まれる。これにより下図のような圏が得られる。
f:id:bitterharvest:20180131191634p:plain

4.3 圏論をベースにしたモノイドの圏

集合論をベースにしたモノイドの圏\(\mathcal{Set}\)が作れたので、次は、圏論をベースにしたモノイドの圏\(\mathcal{Mon}\)を作ることにしよう。これは、簡単で、モノイド圏をそのまま使えばよいので、下図を得る。また、自由モノイドでの射は\(\mu\)とし、そうでないモノイドの射は\(\nu,\nu ’\)とした。
f:id:bitterharvest:20180131191659p:plain

さて、圏\(\mathcal{Mon}\)は、対象間に射を設けられる可能性がある。ここでは、とりあえず、自由モノイドとそうでないモノイドとの間に射を定義しておこう。それらを\(h,h’...\)としよう。

そして、以下の議論で、これらの射\(h,h’...\)が、一意的な準同型写像になることを示し、自由モノイドが圏論での普遍性の一つとなることを示す。

4.4 モノイド圏と集合圏を忘却関手でつなぐ

圏論での世界と集合論の世界をベースにした圏が構成できたので、この二つの圏を関手でつなぐことを考えよう。そこで、圏\(\mathcal{Mon}\)から圏\(\mathcal{Set}\)への関手\(U:\mathcal{Mon} \rightarrow \mathcal{Set}\)を用意しよう(下図)。この関手は、\(\mathcal{Mon }\)の対象を\(\mathcal{Set}\)の対象に写し、射については何も行わない。この関手\(U\)は忘却関手(forgettable functor)と呼ばれる。これは、圏論の世界でモノイドが有していた構造が、集合論の世界に写したときに失われることによる。
f:id:bitterharvest:20180131191737p:plain

忘却関手を用意したので、集合論の世界での対象\(M\)は対象\(N\)に\(U(h)\)で写される。

4.5 モノイド圏と集合圏を忘却関手でつなぐ

今までの説明だけではわかりにくいので、例を用いて具体的に示そう。二進数\(0,1\)の場合を考えてみよう。自由モノイドの方を二進数の数とし、そうでないモノイドの方を2進数の乗算とすると下図を得る。この図で、\(p\)は2進数の数を、\(q\)は2進数の乗算の結果を作り出す関数である。なお、任意の\(a,b \in \{0,1\}\)に対して、\(h([a]++[b])=h(a)*h(b)\)とする。この時、準同型写像になっていることを確認して欲しい。
f:id:bitterharvest:20180131191808p:plain

次の例は、生成元を増やしてモノイドを作る例だ。ここでは、右側のモノイドを生成するときに、生成元に2を加えて、3進数の数を作り出すことを考えよう。この場合には、下図をえる。もちろん、\(q\)は3進数の乗算の結果を作り出す関数である。この時も、任意の\(a,b \in \{0,1\}\)に対して、\(h([a]++[b])=h(a)*h(b)\)とする。そして、準同型写像になっていることを確認して欲しい。
f:id:bitterharvest:20180131191826p:plain

生成元を増やしたので、次は、いくつかの生成元は同じだと見なしてモノイドを作る例を考えよう。次の例は、いずれの生成元に対しても、集合\(N\)の同じ要素に写像されるようにしよう。以下の例では、1に写されるようにした。そして、下図のようになる。同じように、任意の\(a,b \in \{0,1\}\)に対して、\(h([a]++[b])=h(a)*h(b)\)とする。これも、準同型写像になっていることを確認して欲しい。
f:id:bitterharvest:20180131191843p:plain

4.6 普遍性としての自由モノイド

自由モノイドを理解してもらうために、自由モノイドを集合論に基づいて定義し、その性質について説明してきた。

しかし、我々が議論しているのは、集合論の世界ではなくて圏論の世界である。このため、自由モノイドを圏論の世界の中で定義する必要がある。そこで、下図の可換図で示されるように、次のように定義する。
f:id:bitterharvest:20180202095308p:plain

モノイド圏を対象とした圏\(\mathcal{Mon}\)と集合からなる圏\(\mathcal{Set}\)が存在したとする。忘却関手\(U\)は、\(\mathcal{Mon}\)の対象となっているモノイド圏のそれぞれに対して、その対象を\(\mathcal{Set}\)の対象に写すとする。

今、あるモノイド圏\(\mathcal{M}\)から任意のモノイド圏\(\mathcal{N}\)に対して準同型写像\(h\)があったとする。また、これらの圏\(\mathcal{M,N}\)は忘却関手\(U\)で\(\mathcal{Set}\)に写されるので、これらの対象を\(\{M,I\},\{N,I\}\)とすれば、写される先は\(U(\{M,I\}),U(\{N,I\})\)となる。

さらに、生成元{X}を対象とする圏を考えよう。この圏から、先に説明した方法により、モノイドを生成することができる。このため、\(U(\{M,I\}),U(\{N,I\})\)への射が存在するはずである。それらを\(p\),\(q\)としよう。

この時、\(p \circ U(h) = q\)とするような\(h\)が一意的に定まるとき、\(\mathcal{M}\)を自由モノイドと呼ぶ。但し\(U(h):U(\{M,I\})\rightarrow U(\{N,I\})\)である。

自由モノイドが、任意のモノイド圏\(\mathcal{N}\)に対して、準同型写像\(h\)が一意的に定まるといっているので、これは圏論での重要な概念である普遍性であるということができる。

前後が逆になるが、例で挙げたものが自由モノイドになっていることを改めて確認して欲しい。

とろっと柔らかめなスクランブルエッグ

朝の食卓の用意は忙しい。パンを焼いている5分間の間に、コーヒーを沸かし、1~2品のおかずを調理しなければならない。順序だてて、手際のよい処理が必要だ。

このような要求に応えてくれるのは、スクランブルエッグだ。手間暇をかけないでも、楽しんでもらえるメインディッシュとなる。柔らかめにするか、硬めにするかは好みの問題だが、相方はとろっと柔らかめに仕上がったスクランブルエッグを好む。

そこで、今回紹介するのは、柔らかなスクランブルエッグだ。

まず、材料に登場してもらおう。
f:id:bitterharvest:20180128083634j:plain

いたって簡単。二人用ということで、卵を二つ用意する。その他に、牛乳、バター(マーガリン)、塩だ。

卵を割って、小さな椀に入れる。
f:id:bitterharvest:20180128083928j:plain

攪拌して、黄身と白身をよく混ぜる。
f:id:bitterharvest:20180128084050j:plain

牛乳50ccと塩小さじ1/4を混ぜて、さらに攪拌する。
f:id:bitterharvest:20180128084250j:plain

この段階で、パンを焼き始め、コーヒーを沸かすとよいだろう。好みでサラダあるいはソーセージを用意しておくと食卓は豊かになる。

フライパンを中火で温めて、バター10~15gを入れる。
f:id:bitterharvest:20180128084456j:plain

バターがほとんど解けたら、卵を入れる。
f:id:bitterharvest:20180128084542j:plain
f:id:bitterharvest:20180128084650j:plain

フライパンの中央部で、卵が固まり始めてきたら、この部分をへらで脇に寄せる。
f:id:bitterharvest:20180128084818j:plain
f:id:bitterharvest:20180128084849j:plain

この動作を続ける。
f:id:bitterharvest:20180128084922j:plain

全てが脇によったら、出来上がりだ。
f:id:bitterharvest:20180128085028j:plain

皿に盛り、丁度一緒に出来上がった、トースト、コーヒーなどとともに食卓を飾る。
f:id:bitterharvest:20180129074909j:plain
f:id:bitterharvest:20180129074934j:plain

朝の忙しい時間に、5分程度で食事を用意することができ、味も素晴らしいので、我が家では、好んで食べている料理である。

極限-Readerと極限

3.3 \(Reader\)と極限

ここまでの記事で、\(Reader\)の使い方と定義について詳しく説明した。この記事では、さらに理解を進めるために、圏論での極限とのかかわりについて説明しよう。圏論は、様々な数学の分野で共通に成り立っている性質について論じる学問ともみなすことができるが、極限はその一つと言える。いわゆる積と呼ばれる概念が成り立つとき、そこには極限が存在するというのが、普遍に成り立つ性質である。このような性質は普遍性(universal property)と呼ばれ、数学での重要な概念である。

1)\(Reader\)と極限

ここでは、関手\(Reader\)にも、積という概念が成り立ち、そのため、極限と呼ばれる普遍性が成り立つことを説明する。

これまで説明してきたように、関手\(Reader\)は、ある圏の対象としての射\(r: U \rightarrow X\)を、別の圏の対象である射\(Reader \ r\)へと写すものである。また、モナドとしての性質も有している。

少し前の記事で錐について論じた。そこでは、インデックス圏\(\mathcal{I}\)から圏\(\mathcal{C}\)への関手を利用して、錐を作成した。それに倣って、ここでも、圏\(\mathcal{I}\)に二つの対象\(I,J\)を用意することとしよう。また、別の圏\(\mathcal{C}\)は、先ほど説明した対象\(Reader \ r\)で構成されているとしよう。そして、インデックス圏の対象\(I,J\)を\(\mathcal{C}\)へ関手により写すことにしよう。

関手\(D\)によって、それぞれを\(Raeder \ r_I\)と\(Raeder \ r_J\)に写す。ここで、\(r_I:U \rightarrow X_I\), \(r_J:U \rightarrow X_J\)である。

また、関手\(\Delta_C\)によって、それぞれを同じ\(Raeder \ s\)に写す。ここで、\(s:U \rightarrow Y\)である。ただし、\(Y\)は、\(X_I\)と\(X_Y\)のデカルト積とする。即ち、 (Y= (X_I, X_J)\)である。これより次を得る。

\begin{eqnarray}
&& Reader \ s \\
&=& Reader $ U \rightarrow Y \\
&=& Reader $ U \rightarrow (X_I, X_J) \\
&=& Reader $ U \rightarrow (X_I \times X_J) \\
&=& Reader $ (U \rightarrow X_I) \times (U \rightarrow X_J) \\
&=& (Reader $ U \rightarrow X_I) \times (Reader $ U \rightarrow X_J) \\
&=& Reader \ r_I \times Reader \ r_J
\end{eqnarray}
これより、\(Reader \ s\)から\(Reader \ r_I \),\(Reader \ r_J\)に対して射が存在することが分かる(それぞれ第一要素、第二要素を出力とするものである)。

上記の関係を可換図で示すと下図のようになる。
f:id:bitterharvest:20180120102045p:plain

これより、\(Reader r_I\)と\(Reader r_J\)には積\(Reader r_I \times Reader r_J\)が存在することが分かる。従って、錐である。

このため、下図に示すように、任意の\(Reader \ s’\)について、一意的に定まる射が存在するような極限\( Reader \ s\)が存在する。なお、一意的に定まる射は、\(s:U \rightarrow Y\), \(s’:U’ \rightarrow Y\),\(f:U \rightarrow U’\)とした時、\(contramap f\)である。これを可換図で示すと下図のようになる。
f:id:bitterharvest:20180120102106p:plain

2)\(Reader\)の積とHaskellとの関係

それでは、いま説明した\(Reader\)の積はHaskellではどのような時に利用されるのだろうか。それを具体的な例でみてみよう。

いま「誰ですかと問われた」ときに、「トム」と答える関数と「ジェリー」と答える関数を用意したとしよう。

tom :: Reader String String
tom = do
    env <- ask -- gives you the environment which in this case is a String
    return (env ++ " This is Tom.")

jerry :: Reader String String
jerry = do
  env <- ask
  return (env ++ " This is Jerry.")

それでは、二つの関数の積を考えることとしよう。これは、二つを実行し、その結果を一緒にして出力するような関数を用意したらどうであろうか。

tomAndJerry :: Reader String String
tomAndJerry = do
    t <- tom
    j <- jerry
    return (t ++ "\n" ++ j)

さっそく実行してみよう。

*Main> runReader tomAndJerry "Who is this?"
"Who is this? This is Tom.\nWho is this? This is Jerry."

積としての\(Reader\)を実感できたことと思う。

3)余極限との関係

\(Reader\)と極限の関係を示したので、その双対関係にあるものも存在するはずである。そこで、\(Reader\)の矢印の向きを変えた射\(Op\)を次のように定義する。
\begin{eqnarray}
Op \ $ \ r^{op} \\
r : U \rightarrow X
\end{eqnarray}
なお、上記の定義で、射\( r^{op}\)は、射\(r : U \rightarrow X \)の射の矢印の方向を反対にしたものである。

この定義に従うと、\(Op\)の可換図は下図のようになる。
f:id:bitterharvest:20180121095941p:plain
上図で圏\(\mathcal{I}\)はインデックス圏で、\(I,J\)はそこでのふたつの対象である。この対象は、関手\(D\)によって、ある圏\(\mathcal{C}\)の対象となっている射\(Op \ r_I^{op}, Op \ r_J^{op} \)に写される。また、関手\(\Delta_C\)によって、対象となっている一つの射\(Op \ s^{op} \)に写される。

そして、\(Op \ s^{op}\)は次のように変換できる(ここで、\(s: U \rightarrow Y\)である。また、\(\sqcup\)は直和である)。
\begin{eqnarray}
&& Op \ s^{op} \\
&=& Op \ $ \ Y \rightarrow U \\
&=& Op \ $ \ (X_I \sqcup X_J) \rightarrow U \\
&=& Op \ $ \ (X_I \rightarrow U) \sqcup (X_J \rightarrow U) \\
&=& Op \ r_I^{op} \sqcup Op \ r_J^{op}\\
\end{eqnarray}

上図から、対象\(D_I=Op \ r_I^{op}\)と対象\(D_J=Op \ r_J^{op}\)から対象\(C=Op \ s^{op}\)への射が存在することは明らかである。従って、\(Op\)は余錐である。

これにより、\(Op\)は余極限を有するので、次の可換図を得る。
f:id:bitterharvest:20180121101716p:plain

このように、\(Reader\)とその双対である\(Op\)は、圏論での積と余積であり、それぞれ極限と余極限を有することが分かる。

なお、Haskellでは、\(Op\)を\(MonadReader\)と呼ばれるモナドとして定義されている。そして、\(ask\)という関数が、環境変数の値を呼び出すために用意されている。

極限-HaskellでのReaderを定義する

3.2 \(Reader\)を定義する

Haskellでは\(Reader\)を用意している。Control.Monad.Readerというモジュールを読み込めば、使えるようになっている。しかし、このモジュールを理解しようとすると、忍耐力を必要とする。汎用性を高めるために、\(Reader\)が、\(ReaderT\)と呼ばれるデータ型を利用して、定義されているためだ。このため、\(Reader\)を理解しようとすると、別のいくつかのことを理解しなければならない。この作業は、煩わしく、下手をすると、何を理解しようとしていたのかさえ忘れてしまう。

そこで、ここでは、\(Reader\)の理解を助けるために、Haskellでの定義から離れて、必要な事項だけで説明することとしよう。

前回の記事で、\(Reader\)は

type Reader u a 

と理解して、利用した。これは、\(u\)という環境を与えて、\(a\)を出力するというものであった。\(Reader\)は、入力を\(u\)とし\(a\)を出力する関数を、データ型としたものと考えてよい(圏論では、データ型は対象である。関数を対象に選べるのも、圏論の汎用性、有用性を示す証左である)。そこで、次のように定義しよう。

data Reader u a = Reader (u -> a)

Haskellで用いられている用語の説明をしておこう。等式の左側に存在する\(Reader\)は型コンストラクタ(type constructor)と呼ばれる。また、\(u\)と\(a\)は型引数(type argument)と呼ばれる。\(data Reader \ u \ a\)により、データ型\(Reader\)は、データ型\(u\),\(a\)を用いて定義されていることを示す。

等式の右側の\(Reader\)は値コンストラクタ(value constructor)あるいはデータ・コンストラクタ(data constructor)と呼ばれる。これに続く\( \ u \rightarrow \ a\)は型と呼ばれる。そして、関数となっているので、特に、代数的データ型と呼ばれる。

上の定義を用いて、データ型\(Reader\)の値をいくつか求めて見よう。

Prelude> data Reader r a = Reader (u -> a)
Prelude> a = Reader $ \u -> 0.5 * u -- 数値を入力し、その半分を求める関数。もちろんa = Reader (*0.5)と入力しても同じ
Prelude> :t a
a :: Fractional a => Reader a a
Prelude> b = Reader $ \u -> show u – 数値などを入力し、その文字列を求める関数
Prelude> :t b
b :: Show a => Reader a String
Prelude> c = Reader $ \u -> show (u* u) – 数値を入力し、その2乗の文字列を求める関数
Prelude> :t c
c :: (Show a, Num a) => Reader a String
*Main> d :: Reader Int String ; d= Reader $ \u -> show ((*2) u) – 正数を入力し、その2倍の文字列を求める関数。これはd = Reader $ show . (*2)と同じ
*Main> :t d
d :: Reader Int String

1)ファンクタとしての定義

データ型が用意できたので、このデータ型を用いる関数を用意しよう。ファンクタから始めよう。

関手\(Reader\)は、与えられた関数を\(Reader\)というコンテナで包むものである。

データ型が用意できたので、このデータ型を用いる関数を用意しよう。ファンクタから始めよう。ファンクタでは、\(fmap \ f\)という関数を用意する必要がある。

圏\(\mathcal{C}\)は、関手\(Reader \)によって圏\(\mathcal{C’}\)に移されるものとしよう。そして、圏\(\mathcal{C}\)は、射の集まりで構成され、それらは\(U\)から\(X\)への射(\(r:U \rightarrow X\))としよう。また、任意の射\(r\)は、関手\(Reader\)によって圏\(\mathcal{C’}\)の射にコンテナをかぶせられた射\(Reader \ r\)に写されるものとしよう。

なお、コンテナをかぶせられた射は、関数\(runReader \ (Reader \ r) \ u\)を実行することにより、\(r(u)\)の値を出力する。
これらの関係を示すと下図になる。
f:id:bitterharvest:20180114104956p:plain
次に、圏\(\mathcal{C}\)内に別の射\(f\)が存在し、これは、射\(r\)を別の射\(s\)に写すものとする。このとき、\(s = f . r\)となる。また、\(f\)を関手\(Reader\)で写したものを\(fmap \ f\)としよう。これらから、次のような可換図を得る。
f:id:bitterharvest:20180114105557p:plain
プログラムで示した例\(r =(*2),f=show\)の可換図を求めると下図のようになる。
f:id:bitterharvest:20180114111313p:plain
それでは、Haskellでのファンクタの定義を示そう。

instance Functor (Reader r) where
  fmap f (Reader r) = Reader $ f.r

なお、ファンクタとして定義するとき、\(Reader\)が関手なので、\(r\)をつけずに、

instance Functor Reader where
  fmap f (Reader r) = Reader $ f.r

と定義しそうになるが、\(fmap\)の定義において、\(Reader\)ではなく\(Reader \ r\)を用いる必要があるので、\(r\)をつけないと誤りとなる。

2)モナドとしての定義

次はモナドでの関数を定義しよう。本論に入る前に、モナドがどのような数学的な概念であったかを復習しておこう。モナドとは、ある圏\(\mathcal{C}\)のモナド\(M\)とは、\(\mathcal{C}\)の対象\(A\)をコンテナ\(M\)で包んだものであり、それは\(M \ A\)と記述される。モナドには2つの関数が用意されていて、一つはモナドを作り出すもの、他の一つは、モナドからモナドを作り出すものである。二つの関数の用意の仕方にはいろいろあるが、Haskellでは\(return\)と\((>>=)\)を用意することになっている。それでは、下図を用いながら説明しよう。
f:id:bitterharvest:20180114111628p:plain

\(return\)は圏\(\mathcal{C}\)の対象\(A\)をモナドとしてのコンテナで包むことである。これを、\(return\)の型シグネチャで確認しよう。

:t return
return :: Monad m => a -> m a

例をいくつか挙げておこう。[ ] はモナドなので、整数から各括弧の付いたモナドを作成してみよう。

Prelude> a = return 3 :: [Int]
Prelude> a
[3]
Prelude> :t a
a :: [Int]

\(Maybe\)もモナドなので、整数から\(Maybe\)のモナドを作成してみよう。

Prelude> b = return 3 :: Maybe Int
Prelude> b
Just 3
Prelude> :t b
b :: Maybe Int

次の関数は、\((>>=)\)である。これは、二つの引数(\(M \ A,F\)))が与えられる。一つはモナドの値である。もう一つは関数である。この関数\(F\)は少し変わっていて、モナドにする前の値、即ちコンテナに包まれる前の値\(A\)を用いて、これをモナド\(M B\)に変換する。式で表すと、\(F: A \rightarrow M \ B\)である。型シグネチャで確認しておこう。

Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

それでは、いくつかの例を見ていこう。まず各括弧の場合だ。\(f\)の関数は、整数が与えられたならば、それに4を加えて各括弧でくくることにしよう。次のようになる。

Prelude> a = return 3 :: [Int]
Prelude> f = \r -> [r + 4]
Prelude> c = a >>= f  -- 中置関数として
Prelude> c
[7]
Prelude> d = (>>=) a f  -- 前置関数として
Prelude> d
[7]

次は\(maybe\)の例を示そう。整数が与えられた時、それを2倍にし、モナド\(Maybe\)にして返す関数を考えよう。

Prelude> a = return 3 :: Maybe Int
Prelude> f = \x -> Just $ (*2) x 
Prelude> (>>=) a f
Just 6

モナドに慣れてきたので、\(Reader\)に対しても、\(return\)と\((>>=)\)を定義することとしよう。

まず、\(return\)から始めよう。次のように定義されている。

return x = Reader $ \u -> x

上の定義では、\(u\)の値に関わらず、\(x\)の値が定まるので、\(\backslash u -> x\)は定数関数となる。

いくつかの例を挙げてみよう。 \(return\)で出力値を与えて、\(runReader\)で実行するだけの簡単なものだ。

*Main> a = return 3 :: Reader Int Int
*Main> :t a
a :: Reader Int Int
*Main> runReader a 6
3
*Main> b = return 3 :: Reader String Int
*Main> :t b
b :: Reader String Int
*Main> runReader b "Three"
3
*Main> c = return "Jack" :: Reader String String
*Main> :t c
c :: Reader String String
*Main> runReader c "Betty"
"Jack"
*Main> 

次は、\(>>=\)である。モナドでの定義は次のようになっている。

*Main> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

これをReaderで置き換えると次のようになる。

*Main> :t (>>=)
(>>=) :: Reader r -> return . s -> reader g

それでは、下図を参考にしながら、(>>=)を作成していこう。
f:id:bitterharvest:20180115092811p:plain

\( ( >>=)\)は基本的には、二つの関数の結合である。上の図であれば、\(Reader \ r\)と\(Reader \ s\)との結合である。\(Reader \ r\)を実行して、その出力を\(Reader \ s\)の入力として渡せばよい。入力となる値は、\(runReader (Reader \ r) \ u\)である。しかし、\(Reader \ s\)はこれを受け取れるようにはなっていないので、可能な形に変換する必要がある。それには、受け取った入力を\(s\)で変換し、それを戻す定数関数を作ればよい。これをしてくれるのは、先ほど示した\(return\)である。そこで、\(return . s\)とすればよい。従って、\(>>=\)への入力は、\(Reader \ r\)と\(f=return . s\)となる。

\(>>=\)からの出力は、\(Reader \ g\)である。そして、\(g\)は、入力を\(\backslash u\)とし、出力を\(runReader (f \ (runReader (Reader \ r) \ u)) \ u \)とする関数である。

従って、\(>>=\)は、\(Reader \ r\)を\(x\)で置き換えると次のように定めることができる。

x >>= f  = Reader $ \u -> runReader (f (runReader x u)) u

少し、用いてみよう。\(r=(*2),s=show\)の場合には、次のようになる。

*Main> a = Reader (*2) >>= return . show
*Main> runReader a 5
"10"

\(r=show,s=length\)の場合には、次のようになる。これは、入力した数字の桁数を求める関数である。

*Main> b = Reader show >>= return . length
*Main> runReader b 2018
4

最近のHaskellでは、モナドの上位クラスにアプリカティブが用意されている。従って、\(Reader\)と定義するためには、これも必要となる。ここでは、説明を省いて、アプリカティブを含めて、\(Reader\)に関連する定義を示そう。

import Control.Monad
import Control.Applicative

data Reader u a = Reader (u -> a)

instance Functor (Reader r) where
  fmap f (Reader r) = Reader $ f . r     

instance Applicative (Reader r) where
  pure x          = Reader $ \u -> x
  (Reader f) <*> (Reader x) = Reader $ \u -> (f u) (x u)

instance Monad (Reader r) where
 return x = Reader $ \u -> x
 x >>= f  = Reader $ \u -> runReader (f (runReader x u)) u

runReader :: Reader u a -> u -> a
runReader (Reader f) u = f u

ask :: Reader a a
ask = Reader $ \u -> u

メジナの香草焼き

お正月の料理にも飽きた正月8日に、例によって近くの超安売りスーパーに出かけた。我が家は、東京都に属しているのだが、遠くないところにある境川を越えると神奈川県になる。隣県側は製造関連の企業が多いことで知られていた地域だ。かつては、軒並み大きな工場が展開していたが、それらの跡地は大型のスーパーに代わっている。それでも、まだ、いくつかの大企業が残っていて、そこでは外国人の労働者がたくさん働いている。

スーパーで見かける彼らの言葉を聞いていると、南米の出身者だと分かる。中には、日本人と変わらない顔立ちの人たちも見かける。彼らの食習慣に合わせて、この大型スーパーでは、他では見かけないような大切りの肉が売られている。我々も、時々、とても大きなステーキ用の牛肉や、厚みのある豚ステーキ用の肉を購入している。

魚売り場にも、近海で取れたおいしそうな魚が、種類はそれほど多くはないが、時々売られている。この日は、いつもより多く、5種類を超えるほどの魚があった。その中に、体形の美しいメジナを見つけた。しかも、うれしいことに朝捕りで、とても新鮮だ。お刺身がおすすめだろうが、お正月料理が続いたので和食は食傷気味になっていた。もったいないような気もしたが、この日の夕飯を香草焼きで正月気分を一掃しようと考えた。

今回紹介するのは、その時のメジナの香草焼きだ。いつもの例に習って、材料たちに登場してもらおう。
f:id:bitterharvest:20180111095531j:plain

とてもシンプルだ。主材料は、もちろんメジナだ。背中からお腹にかけて幅のある魚だ。ウィキペディアで調べたら、スズキ目メジナ科となっていた。タイはスズキ目タイ科なので、お互いに親戚同士なのだろう。釣りをしないのでよく知らないのだが、釣人にとっては、人気の魚だそうで、「磯釣りの王様」と呼ばれている。

副材料は、魚の生臭さを消す役割も担わせて、玉ねぎ、トマト、ニンニクだ。その他に、ローズマリー、ハーブ入り岩塩、オリーブオイルだ。

メジナは鱗と内臓を取り除く。鱗はタイのそれと比較するととても柔らかい。タイの鱗を撮るときは大根を利用するが、メジナの場合には柔らかすぎて無理かもしれないので、包丁の刃先をなでるようにして、取り除いた。また、内臓からは肉のような塊が出てきた。食べた小魚が胃の中で消化の途中だったのだろう。
f:id:bitterharvest:20180111095600j:plain

玉ねぎ(1個)、トマト(1個)、ニンニク(2かけ)を細切りにする。
f:id:bitterharvest:20180111095637j:plain

次の作業は、岩塩の刷り込みだ。
f:id:bitterharvest:20180111095708j:plain

たまたま、旅行のお土産にもらったイタリア産のものがあったので、これを用いた。耐熱皿にメジナをのせ、まず片面にハーブ入り岩塩を小さじ半分程度刷り込み、ひっくり返して同じことをし、最後に、お腹にも刷り込む。

次の作業は野菜をのせることだ。最初に、お腹に、玉ねぎ、トマト、にんにくを入れる。
f:id:bitterharvest:20180111095754j:plain

野菜の残りを魚の上にのせる。
f:id:bitterharvest:20180111095824j:plain

200度にしたオーブンで30分ほど焼く。
f:id:bitterharvest:20180111095912j:plain

そして、食卓に供する。
f:id:bitterharvest:20180111095933j:plain

この日は、コーンスープも作った。美味しい西洋料理を楽しむことができ、正月気分から抜け出すことに成功した。

極限-HaskellでのReaderについて

3.Haskellでの極限と余極限

これまでの記事で、極限と余極限の説明をしてきた。数学的な記述が主で、Haskellを学ぼうとしている人は、役に立たないなと感じたことだろう。圏論での積や余積は、乗算や加算、あるいは、論理積論理和と関係があることは、直感でもわかる。しかし、これらのために、わざわざ圏論まで持ち出す必要はないと思われただろう。

そこで、今回は、日常的に使われるることはないが、Haskellの奥深さだけではなく、両者の繋がりを巧みに伝えてくれるHaskellの秘密兵器を紹介しよう。

3.1 \(Reader\)を理解しよう

今回、紹介する話題は\(Reader\)である。これまでも説明してきたので、理解されている方も多いと思うが、初めてという方もいることと思う。

\(Reader\)は、なかなか理解しにくいので、手始めにどのような機能を有していて、どのように利用されているかについて説明しよう。

Haskellでは\(Reader\)は次のように定義されている。

type Reader u = ReaderT * u Identity

この定義だと、\(ReaderT\)を理解することが必要になるので、手っ取り早く\(Reader\)を理解することはできない。そこで、上記の定義をとりあえず置いておき、次のように理解しておこう。

type Reader u a

ここで、\(u\)は環境と呼ばれるもので、\(a\)は環境から作り出される値である。少し、利用してみよう。

\(Reader\)には、\(return\)という関数が用意されているので、これを用いて\(Reader\)の値を作り出してみよう。

Prelude> import Control.Monad.Reader
Prelude Control.Monad.Reader> a = return 3.5 :: Reader String Double

それでは、\(return\)によって戻された値の型を確認しておこう。

Prelude Control.Monad.Reader> :t a
a :: Reader String Double

他にもいくつか試してみよう。

Prelude Control.Monad.Reader> b = return "A Happy New Year." :: Reader String String
Prelude Control.Monad.Reader> :t b
b :: Reader String String
Prelude Control.Monad.Reader> c = return 3 :: Reader Int Int 
Prelude Control.Monad.Reader> :t c
c :: Reader Int Int

\(Reader\)には、\(runReader\)という関数も用意されている。この関数は、\(Reader\)の値と、環境の値とを入力すると、\(Reader\)の2番目の型変数に対応した値を出力してくれる。まずは実行例を示そう。

Prelude Control.Monad.Reader> a = return 3.5 :: Reader String Double
Prelude Control.Monad.Reader> d = runReader a "Please, execute it."
Prelude Control.Monad.Reader> d
3.5
Prelude Control.Monad.Reader> :t d
d :: Double

当然、環境の値のデータ型が異なると実行されない。

Prelude Control.Monad.Reader> runReader a 3
<interactive>:22:13: error:
    ? No instance for (Num String) arising from the literal ‘3’
    ? In the second argument of ‘runReader’, namely ‘3’
      In the expression: runReader a 3
      In an equation for ‘it’: it = runReader a 3

\(runReader\)の型シグネチャは次のようになっている。

runReader :: Reader u a -> u -> a

1) 預金残高と利息

\(Reader\)の性格が分かってきたところで、一般的な使い方を示そう。これまで説明しなかったが、\(Reader\)はモナドである。従って、通常のプログラミング言語と同じように、逐次的に命令の列を用意することが可能である。多く用いられているのは、環境を入力して、それに基づいて出力するというものである。

環境を入力する関数として\(ask\)が用意されている。これを利用していくつかの例を示そう。最初の例は、年利率で2%、10万円の定期預金をした時に、\(r\)年後の残高を求めることにしよう。この例では、\(r\)が環境であり、残高が出力である。

これは次のようなプログラムになる。

import Control.Monad.Reader

amount :: Reader Int Double
amount = do
  year <- ask -- 何年間預金したかを入力する
  return (100000 * 1.02 ** (fromIntegral year)) --10万円預金したときの残高を出力

このプログラムを実行してみる。3年後の残高を求めてみよう。

Prelude> :load "amount.hs"
[1 of 1] Compiling Main             ( amount.hs, interpreted )
Ok, modules loaded: Main.
*Main> runReader amount 3
106120.79999999999

3年後には、6千円以上の利息が付いていることが分かる。今の銀行預金の利率もこのくらいならば、預金しがいもあるというものだ。

それでは、利率が3%になった時の比較をしてみよう。プログラムは次のようになる。

import Control.Monad.Reader

amount2 :: Reader Int Double
amount2 = do
  year <- ask -- 何年間預金したかを入力する
  return (100000 * 1.02 ** (fromIntegral year)) --2%の年率で10万円預金したときの残高を出力

amount3 :: Reader Int Double
amount3 = do
  year <- ask -- 何年間預金したかを入力する
  return (100000 * 1.03 ** (fromIntegral year)) --3%の年率で10万円預金したときの残高を出力

amount2vsAmount3 :: Reader Int String
amount2vsAmount3 = do
  a2 <- amount2                                 --2%の年率を実行
  a3 <- amount3                                 --3%の年率を実行
  return ( "2%: " ++ show a2 ++ " vs " ++ "3%: " ++ show a3)

上のプログラムに見るように、比較のプログラム\(amount2vsAmout3\)は、2%と3%のプログラム\(amoun2\)と\(amount3\)を実行させるだけだ。

さて、これを用いて4年間預けたときでの比較をしてみよう。

runReader amount2vsAmount3 4
"2%: 108243.216 vs 3%: 112550.881"

3%にはさらに魅力を感じることだろう。4年間預けておけば、利息だけで小旅行ができそうだ。

2)米国からドイツへ移動したときの単位の変換

\(Reader\)の使い方に慣れてきたが、さらに、ダメ押しでもう一つ例を上げてみよう。今度は、米国からドイツへ移動したときの単位の変換だ。

まず、変換のためのデータ型\(Conv\)を新たに用意しておこう。次のようにした。

data Conv = Conv

次にマイルからキロメートルへの変換のプログラムを作ろう。1マイルは1.6㎞なので次のようにする。

m2k :: Reader Conv String
m2k = do
  env <- ask
  return "One mile is equal to 1.6 km."

同様にポンドからキログラムへの変換プログラムを作ろう。1ポンドは0.45kgであるので、次のようになる。

p2k :: Reader Conv String
p2k = do
  env <- ask
  return "One pound is equal to 0.45 kg."

さらに通貨を変換してくれるプログラムを作ろう。1ドルは0.83ユーロとすると、次のようになる。

d2e :: Reader Conv String
d2e = do
  env <- ask
  return "One Dollar is equal to 0.83EUR."

最後にすべての変換を与えてくれるプログラムを作ろう。これは上記のプログラムを用いて次のようになる。

u2g :: Reader Conv String
u2g = do
  length <- m2k
  weight <- p2k
  money <- d2e
  return (length ++ " \n " ++ weight ++ " \n " ++ money)

それではこれを実行してみよう。

Prelude> :load "Reader.hs"
[1 of 1] Compiling Main             ( Reader.hs, interpreted )
Ok, modules loaded: Main.
*Main> runReader u2g Conv
"One mile is equal to 1.6 km. \n One pound is equal to 0.45 kg. \n One Dollar is equal to 0.83EUR."

このプログラムは、\(u2g\)というファイルを読みだしているようには見えないだろうか。このように、\(Reader\)には、データベースやファイルを読みだしているように感じさせる機能がある。

次回は、\(Reader\)を定義して、さらに理解を深めよう。

極限-余極限の例

2.2 余極限の抽象化

前回と同じように、余極限を抽象化してみよう。ここでは、下図のように、三つの圏を用意する。最初の圏\(\mathcal{C_0}\)は、余錐の頂点からなる圏だ。次の圏\(\mathcal{C}\)は余錐で構成される圏だ。最後の圏\([\mathcal{I,C}]\)はインデックス圏から余錐を構成したときの自然変換を射とする圏だ。
f:id:bitterharvest:20180105154344p:plain

今、圏\(\mathcal{C_0}\)で、対象\(C,C’\)の間の射は\(f:C \rightarrow C’\)であったとする。圏\(\mathcal{C_0}\)から圏\(\mathcal{C}\)には、ある関手によって、上図の中ほどのように写されたとする。同様に、圏\(\mathcal{C_0}\)から圏\([\mathcal{I,C}]\)には上図の右側のように写されたとする。

前回と同じように、圏\(\mathcal{C}\)と圏\([\mathcal{I,C}]\)の間では上図のように可換が成り立つ。従って、圏\(\mathcal{C}\)で余錐を与えると、圏\([\mathcal{I,C}]\)からそれはどのような性格を有しているのかを得ることができる。

上の図は、下のような可換図に書き換えることができる。
f:id:bitterharvest:20180105154420p:plain

それでは、上の図を用いて、余極限のいくつかの例を挙げることにしよう。なお、例をあげるとき、対象\(C\)と余極限である対象\(ColimD\)とは一緒であるとする。

2.3 余極限の例

1)始対象

最初の例は、下図に示すように、余錐の上面が存在しない場合である。
f:id:bitterharvest:20180105154453p:plain

上面が存在しないので、圏\(\mathcal{C}\)では余錐は頂点だけからなる。また、余極限の余錐\(C=ColimD\)から任意の余錐\(C’\)に対して、一意的に定まる射\(m’\)が存在する。これを、圏\([\mathcal{I,C}]\)に写すと下図のようになる。これから、関手\(\Delta_C\)から任意の関手\(\Delta_C’\)への射が一意的に定まることから関手\(\Delta_C\)が始対象であることが分かる。

2)余積

それでは、余錐が独立した
2頂点で構成されている場合を考えよう。下図のようになる。
f:id:bitterharvest:20180105154554p:plain

圏\(\mathcal{C}\)では、2頂点の余極限の余錐は上図の左側のようになる。これに、任意の余錐を加えたのが真ん中の図である。これを圏\([\mathcal{I,C}]\)に写したのが右図である。\(\Delta_C\)から\(\Delta_{C’}\)への射が一意的に定まることから、下図に示すように余積となる。
f:id:bitterharvest:20180105154615p:plain

3) コイコライザ

それでは、2頂点と、その間を同じ方向に二つの射が結んでいるような余錐について考えてみよう。下図のようになる。
f:id:bitterharvest:20180105154638p:plain

錐の場合にはイコライザであったが、今回の場合はコイコライザになる。コイコライザの定義は次のようになっている。
「コイコライザの定義」適当な対象\(Q\)と射\(q:Y \rightarrow Q\)で\(q \circ f = q \circ g\)を満たすものが存在し、任意の対象\(Q’\)と射\(q’:Y \rightarrow Q’\)の組\((Q’,q’)\)で、\(q’ \circ f = q’ \circ g\)が与えられた時、射\(u:Q \rightarrow Q’\)が\(u \circ q=q’\)を満たすものが一意的に存在するとき、\((Q,q)\)の組をコイコライザという(下図参照)。
f:id:bitterharvest:20180105154724p:plain

3) 押し出し

最後は上面の頂点が3個あり、一つから他の二つの頂点へ射が与えられている場合だ。これは下図のようになる。
f:id:bitterharvest:20180105154944p:plain

これと双対の関係にあるものは、引き戻し(pullback)と呼ばれ、データベースを応用例として紹介した。
余極限の場合には、押し出し(pushout)と呼ばれ、共通のドメインを有する射の対が極限となる。

Puzzleと呼ばれるホームページに、押し出しを説明するのにちょうどよい例が上がっていたので、これを用いて説明しよう。
f:id:bitterharvest:20180105155727p:plain

ホームページには、鹿なのだろうか、2匹の小動物が、角をくっつけあって遊んでいる木工パズルが紹介されている。

小動物をそれぞれ\(A,B\)、くっつけあっている部分を\(Z\)とすると、上図のパズルが完成した様子は\(A \cup_f B\)と記述することができる。ここで、\(\cup_f \)は\(Z\)の部分で接合しているという意味である。なお、もう少し正確に説明すると、\(Z\)は\(B\)の部分集合で、接合される部分である。\(f:Z \rightarrow A\)は\(A\)の接合部分を求める関数である。\(f(Z)\sim Z\)は\(f(Z)\)と\(Z\)が同値であることを示すもので、これにより、接合部分がくっつけられたことを意味する。

接合部\(Z\)は、\(A\)と\(B\)のぞれぞれの部分となっている。\(Z\)から\(A\)と\(B\)への射がこれを表している。また、\(A\)と\(B\)は、パズルが完成したときの構造物\(A \cup_f B\)の一部となっているので、それぞれから射が存在する。

上記で用いたのはattaching functionと呼ばれるものである。これが押し出しの例になるということを示したが、この関数についてさらに知りたい人は、WikipediaでAdjunction spaceを参照するとよい。

次の記事では、Haskellでの極限と余極限の利用例を示そう。

極限-余錐と余極限の定義

2.余極限

極限と双対の関係にあるのが、余極限である。

2.1 余錐と余極限の定義

前回までの記事で、極限について説明してきた。極限は錐(cone)を用いることで定義した。即ち、ある圏の中で錐が定義できたとする。そのような錐は、複数あっても一つでも構わないが、「どの錐からも一意的な射が存在するようなが存在するとき、このを極限」と呼んだ。

双対は射の向きを反対にすることで得られる。極限の定理では、錐という専門用語を用いているが、余極限では、余錐(co-cone)という専門用語を用いる。錐は頂点(apex)から底面の方に向かって射が射影(projection)されていたのに対し、余錐は、底面の方から頂点に向かって射が射出(injection)される。余錐を用いると、余極限の定義は次のようになる。「どの余錐に対しても一意的な射が存在するような余錐が存在するとき、この余錐を余極限」と呼ぶことにしよう。

上記の言葉だけによる定義を、もう少し、明確にするために、インデックス圏を用いて定義することにしよう。下図のように、インデックス圏\(\mathcal{I}\)と圏\(\mathcal{C}\)が存在したとしよう。
f:id:bitterharvest:20180104103025p:plain

インデックス圏は、説明を簡単にするために、3対象とそれらの間の射および恒等射で成り立っているとしよう(これは一般性を失うものではない。もっと多くの対象が存在したとしても、3対象に分割して考えられることによる)。

圏\(\mathcal{I}\)から圏\(\mathcal{C}\)への関手に設けることにしよう。一つは、圏\(\mathcal{I}\)の全ての対象\(I,J,K\)を圏\(\mathcal{C}\)の同一の対象\(C\)に移す関手\(\Delta_C\)としよう。この関手は、圏\(\mathcal{I}\)の全ての射\(f,g,h\)を圏\(\mathcal{C}\)の対象\(C\)の恒等射に写すことに注意しておこう。

もう一つは、圏\(\mathcal{I}\)の3対象\(I,J,K\)を圏\(\mathcal{C}\)の別々の対象\(D_I,D_J,D_K\)に写したもので、これを関手\(D\)としよう。このとき、圏\(\mathcal{I}\)の射\(f,g,h\)は圏\(\mathcal{C}\)の射\(Df,Dg,Dh\)に移されたものとする。

\(\bf{余錐の定義}\)
関手\(F\)から関手\(\Delta_C\)への自然変換が存在するとき、\(D_I,D_J,D_K\)を面とし、\(C\)を頂点としたものを、余錐と呼ぶことにする。なお、錐の場合には、頂点を上に描いたが、余錐の場合には、頂点を下に描くことにする。従って、\(D_I,D_J,D_K\)によって作られる面は上に来るので、余錐の上面と呼ぶこととする。

\(F\)と\(\Delta_C\)による自然変換が存在するとは、上図では、
\begin{eqnarray}
\alpha_I \simeq \alpha_J \circ Df \\
\alpha_J \simeq \alpha_K \circ Dg \\
\alpha_I \simeq \alpha_K \circ Dh
\end{eqnarray}
が成り立つことである。なお、\(\simeq\)とは、左右の射が一対一に対応すること、即ち、同型であることを意味する。

\(\bf{余極限の定義}\)
圏\(\mathcal{C}\)内の任意の余錐に対して、ある余錐から一意的に定まる射が存在するとき、この余錐を余極限(colimit)と呼ぶ。

次の記事では、余錐の例を挙げるための準備をしよう。

極限-極限の例

1.8 錐の例

今年のブログの締めは、少しでも圏論が身近に感じられるようにするために、錐の例を取り上げよう。

前回のブログで、抽象度の高い、下図のような極限の可換図を示した。
f:id:bitterharvest:20171228132358p:plain

この可換図は、錐を作成すると、それがどのような極限なのかを、示してくれる。

上図で、圏\(C\)は、対象となっているのは錐だけで、射となっているのは錐の頂点間の写像という極めて簡素な圏である。

圏\([\mathcal{I,C}]\)はインデックス圏\(I\)から圏\(C\)への関手によって、錐がどのような構造を持っているかを示した圏である。

それでは、いくつかの例を示そう。

1)終対象

最初は、底面のない錐である。圏\(\mathcal{C}\)があった時、その対象は底面のない錐であったとしよう。その中には、極限と呼ばれる錐\(C’\)が存在する。これを\(LimD\)と記述しよう。これを下図の左側に示す。

そして、圏\(\mathcal{C}\)から任意の対象\(C\)を取り出そう。\(C\)も\(C’(=LimD)\)も錐であり、そして、\(C’\)は極限の錐であることから、\(m:C \rightarrow C’\)となる一意に定まる関数が存在する。そして、\(f:C’ \rightarrow C\)とした時、\(m= contramap \ f\)となる。これらの関係を下図の真ん中に示す。
これらの関係については、下図を参照のこと。錐の頂点は正確に記述すると\(\mathcal{C}(C',LimD)\),\(\mathcal{C}(C,LimD)\)だが、ここでは、簡略して\(C',C\)と記述している。
f:id:bitterharvest:20171221090801p:plain

これを圏\([\mathcal{I,C}]\)に移すと、下図の右側の図となる。これは、任意の対象について成り立っていることから、任意の対象\(\Delta_C\)から\(\Delta_{LimD}\)への射\(contramap \ f\)が存在することとなり、\(\Delta_{LimD}\)は終対象となる(なお、\(contramap \ f \)は圏\(\mathcal{C}\)と圏\([\mathcal{I,C}]\)で同一に記述しているが、構造は似ているものの異なる関数であることに注意しておこう)。

従って、頂点だけの錐は、極限が終対象となることが分かる。
f:id:bitterharvest:20171228132426p:plain

2)積、デカルト積、冪

次の例を示そう。次の圏\(C\)は、底面は有するが、2頂点だけで、辺を持たない錐を対象にしているものとする。それを、下図の右側に示す。

前回の例と同じように、錐\(C,C’\)を定めることにしよう。\(C’=limD\)は極限の錐である。これを示したのが、下図の左側と真ん中である。

そして、これを圏\([\mathcal{I,C}]\)に移してみよう。これは、圏\(\mathcal{C}\)の積であることが分かる。
f:id:bitterharvest:20171229095359p:plain

もう少し、分かりやすく示したのが下図である。
f:id:bitterharvest:20171228132508p:plain

底辺の頂点をさらに具体的にすると下図のようになる。底辺の頂点\(A,B\)が集合である時は、下図の右側に示すように、錐の頂点\(A \times B\)はデカルト積\((A,B)\)となる。

また、底辺の頂点\(X,Y\)を写像ドメインとコドメインと考えると、\(X \times Y\)は定数関数となり、冪\(Y^X\)で表される。(なお、\(X\)から\(Y\)への写像は、\(X\)の集合の要素の数を\(X\)とし、\(Y\)の集合の要素の数を\(Y\)とすると、定数関数の数は、\(Y^X\)となる。これを利用して、関数をこのように冪を用いて表現している)。
f:id:bitterharvest:20171228132529p:plain

3)イコライザ

さらに、例を示そう。次の圏\(C\)は底面に2頂点を有し、一方の頂点から他方の頂点へ二つの射\(f,g\)があるものとする。これを、下図の右側に示す。

前回の例と同じように、錐\(C,C’\)を定めることにしよう。\(C’=limD\)は極限の錐である。これを示したのが、下図の左側と真ん中である。
f:id:bitterharvest:20171228132612p:plain

これを、圏\([\mathcal{I,C}]\)に移すと上図の左側の図になる。

かつて、イコライザの説明をした時に、これを次のように定義した。

「イコライザの定義」適当な対象\(E\)と射\(eq:E \rightarrow X\)で\(f \circ eq = g \circ eq\)を満たすものの組\((E,eq)\)が存在し、任意の対象\(O\)と射\(m:O \rightarrow X\)の組\((O,m)\)で、\(f \circ m = g \circ m\)が与えられた時、射\(u:O \rightarrow E\)が\(eq \circ u = m\)を満たすものが一意的に存在するとき、\((E,eq)\)をイコライザという。

下図に示すように、圏\([\mathcal{I,C}]\)はイコライザの定義そのものである。従って、底辺の2頂点間で同一方向に二つの射が存在するとき、極限はイコライザとなる。
f:id:bitterharvest:20171228132642p:plain

4)引き戻し

さらに、例を示そう。次の圏\(C\)は底面に3頂点を有し、二つの頂点から残りの一つの頂点への写像が存在するものである。それを下図の右側に示す。
f:id:bitterharvest:20171228132709p:plain

この例はデータベースである。具体的に示そう。ある学年の名簿を作成したと仮定しよう。そして、上の図に示すように、錐の頂点はデータベースという対象である。ここには、男子学生、女子学生、組のテーブルがある。男子学生のテーブルには、名前を記載するフィールドと所属する組を示すフィールドがある。女子学生のテーブルも同じである。また、組のテーブルは、クラスの名前を記入する。

データベースからの射が、男子学生、女子学生、クラスと呼ばれる集合で、これらは錐の底辺での頂点となる。そして、頂点間では、男子学生と女子学生からクラスへの射が設けられている。

ここで、射の方向を変えて、クラスの側から男子学生あるいは女子学生を観察したらどのようになるであろうか。例えば、A組に属している男子学生を求めたとしたら、それは、{太郎、次郎、…}というように構成員の集まりとなる。通常、写像はコドメインとなっている集合の要素を与えてくれるが、この場合は、集合を与えてくれる。

このような場合、上図に示すように、男子学生からクラスへの写像の値が一致する男子学生の集合を表すために一本の線を引き、その線上に構成要素を記述する。これは、数学の用語では、ファイバーと呼んでいる。ホモトピーなどを学ぶときなどに、必要とされる概念である。

また、この錐の極限を、引き戻し、あるいはファイバー積という。

宿題:上記のデータベースを極限を用いてHaskellで実現しなさい。

年が明けたら、極限と双対関係にある、余極限について説明する。それでは、良いお年をお迎えください。

ジューシーなターキーをクリスマスに楽しむ

昨年、ブライン液に浸して、ジューシーなターキーを食べることができたので、今年も、これを利用することにした。ただし、昨年は、塩だけしか利用しなかった。砂糖も一緒に用いるとよいという情報も得ていたが、甘くなるのも嫌だなと思い、塩だけでブライン液を作った。

しかし、最近になって、砂糖を用いると水分がより吸収されるという記事を見たので、昨年とは異なるブライン液を作ることにした。

今年も昨年と同じ大きさのミニ・ターキーを仕入れた。
f:id:bitterharvest:20171225095350j:plain
成城石井に電話をして、確保したのが、このターキーである。お店の方で解凍してくれたので、料理する前の日まで冷蔵庫に保存しておき、最終の夜は冷たい家事室に移した。

今年も、孫たちが23日が都合がよいというので、イブのイブにターキーを食べることにした。朝8時から、ブライン液を作り始めた。水3リットルに対して、塩と砂糖をそれぞれ150g加えた。
f:id:bitterharvest:20171225095419j:plain
f:id:bitterharvest:20171225095439j:plain

バケツの中にビニール袋を二重にし、その中に、水、塩、砂糖を注入した。
f:id:bitterharvest:20171225095504j:plain

ターキーを袋から取り出すと、水が大量にこぼれる。これらの水は、本来、ターキーの中にあったものが、解凍に伴って流れ出てきたものだろう。ターキーの肉をぱさぱさにさせる要因だ。ターキーの重さを量ったら2.9Kgであった。
f:id:bitterharvest:20171225095552j:plain

これをブライン液の中に4時間ほど浸した。
f:id:bitterharvest:20171225095610j:plain

昼食後、ローストの作業に取り組んだ。ローストに使う材料は、以下の通りだ。
f:id:bitterharvest:20171225095631j:plain
ターキーに使う野菜は、臭い消しと出汁が主なので、手ごろな野菜を利用するのだが、今年はどの野菜も高く、屑野菜を用いたという気分にはならなかった。
野菜は、セロリ、ニンジン、キャベツ、玉ねぎ、パセリ、ローズマリーだ。さらに、ジャガイモを一緒に蒸かすことにした。

野菜を細かく切って、オーブン皿にもる(ジャガイモの姿も見えるが、これは、取り出して、後から、オーブンの中に入れた)。
f:id:bitterharvest:20171225095731j:plain

ブライン液から取り出したターキーはこのような感じ。
f:id:bitterharvest:20171225095907j:plain
重さをはかってみたら、3.0Kgであった。水分100gを吸ったことになる。昨年は、ブライン液の変化で吸われた水分の量を推定したが、正確ではなかったと思う。今年のほうが正しい量だ。
冷蔵庫に栗の瓶詰があったので、ターキーの内臓に栗を詰めた。
ターキーは、全身を塩胡椒して、オーブン皿にのせた。内臓から取り出した、首と砂肝も一緒にのせた。
f:id:bitterharvest:20171225095937j:plain

ターキーの上には、一箱のバターを手ごろな大きさに切ってのせた。
f:id:bitterharvest:20171225100050j:plain

オーブンを250度にし10分ほど焼いた。その後、温度を下げて焼く。低温の方がおいしく焼けると行く記事を読んだので、去年より10度低くして、170度にした。ポップアップタイマー(写真の真ん中右上にある赤いもの)が上がってくるまで焼いた。これには、1時間40分要した。その間、20分おきぐらいに煮汁をターキーの上にかけた。なお、ジャガイモは、焼きすぎを避けるため、ターキーを焼き始めてから30分後にオーブン皿に加えた。

焼き上がりはこのような感じ。
f:id:bitterharvest:20171225100222j:plain

煮汁を利用して、ターキーにかけるスープを作る。濾した煮汁を鍋に移し、ローリエ一枚とマギーブイヨン一粒をくわえ、煮立ったところで、ウィスキー大匙一杯を加え、アルコールを飛ばして出来上がりだ。
f:id:bitterharvest:20171225100246j:plain
サラダやコーンスープやワインなどと一緒に、孫たちと一緒に5人で食した。
f:id:bitterharvest:20171225100457j:plain

ブライン液から出した後、ターキーは水洗いをしないで、そのまま用いたので、砂糖の甘みが心配であったが、だれも気にしなかった。
孫たちは成長期ということもあり、あっという間に食べられてしまったが、ジューシーで、とても美味しいターキーだった。

極限-錐と極限のさらなる抽象化

1.7 錐と極限のさらなる抽象化

前回の記事で、錐は二つの異なる方法で作成できることが分かった。一つは、錐の頂点から錐の底面への写像を自然変換で与えるものである。もう一つは、求める錐の頂点から極限の錐の頂点への射を与えるものである。

前回は、さらに、この二つの方法は同型の写像を与えることも示した。写像が同型であるので、この間で自然変換を定義できるようになる。求める錐の頂点を\(C\)としたとき、この始自然変換を\(\alpha_C\)で表すことにしよう。図で示すと次のようになる。
f:id:bitterharvest:20171221090641p:plain

ここまでが前回の話である。

それでは、この方法によって、\(C\)を頂点とする錐が得られたとする。この得られた錐から、別の錐\(C’\)を求めるにはどのようにしたらよいであろうか(下図)。
f:id:bitterharvest:20171221090658p:plain

なお、\(C\)と\(C’\)には、\(f:C’ \rightarrow C\)の関係があるものとする。ここで、\(f:C \rightarrow C’\)でないことに注意しておいてほしい。訳はすぐに分かる。

これの可換図を求めると次のようになる。なお、関手は射も写像するが、その時移す側と移される側で向きが同じとき、この関手は共変関手(contravariant)と呼ばれる。それに対して、反対になるときは、関手は反変関手(contravariant)と呼ばれる。そこで、上の図では反変関手なので、\(f\)が移されている先を\(contramap \ f\)とした。
f:id:bitterharvest:20171221090723p:plain

上図の互換図で\(\mathcal{C}(C,LimD)\)は射の集合であるので、それを\(u: C \rightarrow LimD\)と表すことができる。また、\(\mathcal{C}(C’,LimD)\)を\(v: C' \rightarrow LimD\)と表すことができる。ところで、\(f:C' \rightarrow C\)なので、\(v=u \circ f\)となる。

また、\([\mathcal{I,C}](\Delta_C,D)\)は自然変換である。従って、これはインデックス圏の対象\(I\)毎に、\(\mu_I:C \rightarrow D_I\)と表すことができる。同様に、\([\mathcal{I,C}](\Delta_{C'},D)\)を\(\nu_I:C’ \rightarrow D_I\)と表すことができ、先ほどと同様に、\(\nu_I =\mu_I \circ f\)となる。

この関係を示したのが下図である。
f:id:bitterharvest:20171221090801p:plain

この互換図から、圏\(\mathcal{C}\)での頂点を\(C\)とする錐から、圏\([\mathcal{I,C}]\)での頂点\(C’\)の錐を求めるには、圏\(\mathcal{C}\)で\(C’\)に移動してから圏\([\mathcal{I,C}]\)にジャンプしてもよいし、圏\(\mathcal{C}\)から圏\([\mathcal{I,C}]\)にジャンプしてから\(C’\)に移動してもよいと言っている。これを図で表すと以下のようになる。
f:id:bitterharvest:20171221091405p:plain

極限の理論的な話はここまでだが、次回はいくつか例を示そう。

極限-錐と極限の抽象化

1.6 錐と極限の抽象化

前回の記事までで、インデックス圏を用いての粋と極限の求め方を説明してきた。その中では、インデックス圏から粋を作成しようとしている圏へ、三角形のまま移す関手と、一点にまとめる関手を用意した。前者は錐の底辺を形成し、後者は錐の頂点を形成することも説明した。また、インデクス圏に用意するものは三角形に限る必要はなく、多角形でもよいこと、さらには、それを無限とした円でもよいことを説明した。また、錐を構成するする条件として、全ての側面で可換であるということも説明した。

それでは、このようにして出来上がった錐をもう少し抽象化した件の中で考えることにしよう。

抽象化1

錐を作成するとき、インデックス圏\(\mathcal{I}\)と錐を構成する圏\(\mathcal{C}\)を用意し、その間を関手\(C,\Delta_C\)で結んだ。また、関手間に自然変換\(\alpha\)を定義した。

そこで、関手を対象とし、自然変換を射とすることで、新たな圏を定義することができる。これを圏\([\mathcal{I,C}]\)と呼ぶことにしよう。

抽象化2

もう一つは極限の錐を利用するものだ。圏\(\mathcal{C}\)の中に作られた錐を対象とし、それぞれの錐の頂点から極限の錐の頂点への写像を射とするものだ。これをそのまま圏\(\mathcal{C}\)としよう。ただ、それぞれの錐では、その側面が可換になっているということに注意しておこう。

この二つの抽象化を図で表すと次のようになる。
f:id:bitterharvest:20171219164902p:plain

直感的にこの二つは、同じであると感じであろう。左の方は、関手\(\Delta_C\)と\(D\)が与えられると、錐が形成されると説明している。右の方は、極限の錐の頂点\(LimD\)が与えられているとする。そして、ある対象\(C\)から極限の錐の頂点への射\(m\)が与えられたとき、その対象\(C\)は、錐の頂点になると言っている。従って、両方とも同じことを言っている。そこで、これをもう少し厳密にして、定理として示すことにしよう。

二つの抽象化は同型である

上の図を一般化して、下図のようにする。
f:id:bitterharvest:20171219164924p:plain

まず左側について説明する。\(\Delta_C\)と\(D\)の間の自然変換は、一般には、一つとは限らず複数となる場合もあり、集合となる。そこで、これを\([\mathcal{I,C}](\Delta_C,D)\)としよう。なお、自然変換がない場合もあるが、この場合には、錐を構成できないということになる。

次に右側を説明する。対象\(C\)から極限の錐の頂点\(LimD\)への射も、同じように一つとは限らず複数存在する場合もある(無の場合もあるが、この場合には錐を構成できないということになる)。この集合を\(\mathcal{C}(C,LimD)\)としよう。なお、\(C\)については錐であるという条件を課する必要はない。これは、後で分かるが、射がある場合には、必然的に、錐となる。

それでは、右側と左側は同型であることを示すこととしよう。

1) まず、左側から右側が導かれることを示す。これは、\([\mathcal{I,C}](\Delta_C,D)\)ならば\(C\)から\(LimD\)への射があることを示せばよい。

これは簡単である。関手\(\Delta_C\)と\(D\)によって錐を作成したとすると、その錐の頂点から極限の錐の頂点へ射\(m\)が存在することは、極限の錐の定義から明らかである。従って、\(m \in (\mathcal{C}(C,LimD)\)となるので、証明できたこととなる。

2) 次に、右側から左側が導かれることを示す。\(\mathcal{C}(C,LimD)\)であるならば、\([\mathcal{I,C}](\Delta_C,D)\)であることを示せばよい。

これには、射の集合\(\mathcal{C}[C,Limd]\)から任意の一つの射を取り出す。これを\(m\)としよう。このとき、\(C\)を頂点とする錐が構成できれば、証明できたことになる。これは、錐の辺が構成されることを、元々のインデックス圏の対象ごとに示せばよい。

下図に示すように、インデックス圏の対象\(I\)について考えてみよう。極限の錐にはその定義から射\(\beta_I\)が存在する。従って、\(m\)と\(\beta_I\)を結合したものは、射となるので、これを\(\alpha_I\)とする。これは、とりもなおさず、\(C\)を頂点とする錐の辺である。全てのインデックスの対象に対して、上記の操作を施すことで、\(C\)を頂点とした錐を実現することができ、証明は終わりである。
f:id:bitterharvest:20171219164943p:plain

極限-極限の定義

1.5 極限の定義

前回の記事で、インデックス圏から錐を作成する方法を示した。
f:id:bitterharvest:20171218102050p:plain

上図で、関手\(D\)によって、インデックス圏\(\mathcal{I}\)での三角形(頂点が対象、辺が射)が、圏\(\mathcal{C}\)に移され、これから作成される(三角)錐の底辺を形成する。この底辺の各頂点は\(\mathcal{I}\)の対象を、また、各辺は\(\mathcal{I}\)の射を、関手\(D\)によって、それぞれ移したものである。

また、上の図で、圏\(\mathcal{I}\)の三角形の頂点、即ち、対象の全てを、関手\(\Delta_C\)によって、一つの対象\(C\)に移した。このとき、圏\(\mathcal{I}\)の三角形の辺、即ち、射の全ては、\(\Delta_C\)によって、\(C\)での恒等射\(Id_{\Delta_C}\)に移される。

関手\(\Delta_C\)と関手\(D\)の間で、自然変換\(\alpha=\{\alpha_I,\alpha_J,\alpha_K\}\)が存在するなら、三角錐\(C,D_I,D_J,D_K\)が構成され、その側面は可換となっている。即ち、\(Df \circ \alpha_I \simeq \alpha_J\), \(Dg \circ \alpha_J \simeq \alpha_K\),\(Dh \circ \alpha_I \simeq \alpha_K\)である。

同様に、関手\(\Delta_C’\)からも同じように側面が可換となっている三角錐を構成できる。

このような三角錐がいくつもできるであろう。その中で、極限と呼ばれるものを定義することにしよう。得られた三角錐の中から一つの頂点を選び、これを\(LimD\)と呼ぶことにしよう。\(LimD\)を頂点とする三角錐が次の条件を満たす時、極限という。なお、この三角錐での自然変換を\(\beta=\{\beta_I,\beta_J,\beta_K\}\)とする。

[条件] 任意の(三角)錐の頂点\(C\)に対して
1. 一意的に定まる射\(m: C \rightarrow LimD \)が存在し、
2. 圏\(\mathcal{I}\)の全ての対象\(i\)に対して、\(\beta_I \circ m = \alpha_I\)となる。即ち、上記の図の場合には、\(\beta_I \circ m = \alpha_I\),\(\beta_J \circ m = \alpha_J\),\(\beta_K \circ m = \alpha_K\)である。

説明では、理解しやすくするために、インデックス圏を三角形としたが、多角形でも構わない。さらには、辺の数が無限となった円でも構わない。

なお、このような射\(m\)は分解射(factorization)と呼ばれる。

また、\(\beta_I,\beta_J,\beta_K\)は、専門用語を用いれば、モノ射である。モノ射は次のように定義されている。射\(f:X \rightarrow Y\)がモノであるとは、任意の射\(g_1,g_2:Z \rightarrow X\)に対して、\(f \circ g_1= f \circ g_2\)であるならば、\(g_1=g_2\)が成り立つ。