リストがモナドであることを確かめてみた。

きっかけは、モナドの定義とか - 檜山正幸のキマイラ飼育記 (はてなBlog)あたりが分かったような分からないようなもやもやした気分になったから。
わからないのは一方的にこっちの勉強が足りないからなので、考えてみることにした。
とりあえず、リストは本当にモナドか?というところをやってみた。


まず、手持ちの圏論の本のモナドの定義を眺めてみたが、エントリにあったKleisli tripleと結びつかなかった。
ということで、リストが(圏論の本の定義の)モナドかどうか自分で確かめることにした。

リストはモナド

結論だけ。
リストの圏Dは以下のように決めればよい。イメージしやすさのため、Double(実数)のリストとする。

対象:D_0, D_1, D_2, \ldots

ここで, D_0 :=Double(実数の集合), D_1 := D_0のリストの集合, D_2 := D_1 のリストの集合(==実数のリストのリストの集合), \ldots D_n := D_{n-1}のリストの集合, \ldots*1

射:以下のようなもの。例で説明する。値の表現はyaml風。

flatten(concat, join)系

flatten :: D3 -> D2, [[[1,2,3],[4,5]],[[6,7]]] -> [[1,2,3],[4,5],[6,7]]

1 2 3 4                      1   2 3  [<=入れ子の深さ]
-                            -
  -                             
    -                            -
      - 1                          - 1
      - 2                          - 2
      - 3  flatten(concat)         - 3
    -     =================>     -
      - 4                          - 4
      - 5                          - 5
  -                             
    -                            -
      - 6                          - 6
      - 7                          - 7

要するに、第2列の「-」を縦に消していくのがflatten。
また、第3列を消すのはmap.flattenと書ける。第4列があれば、map.map.flatten。
以上のようにして、各Diについて定義できるだけの(map^n).flattenを定義しておく。

singleton(return)系

singleton :: D3 -> D4, [[[1,2,3],[4,5]],[[6,7]]] -> [[[[1,2,3],[4,5]],[[6,7]]]]

1 2 3 4                      1 2 3 4 5  [<=ネストの深さ]
                             -
-                              -        
  -                              -      
    -                              -    
      - 1                            - 1
      - 2                            - 2
      - 3  singleton(return)         - 3
    -     ===================>     -    
      - 4                            - 4
      - 5                            - 5
  -                              -      
    -                              -    
      - 6                            - 6
      - 7                            - 7

要するに、第1列のさらに左に「-」を入れるのがsingleton。
第1列と第2列の間に-を入れるのはmap.singletonと書ける。

map.singleton :: D3 -> D4, [[[1,2,3],[4,5]],[[6,7]]] -> [[[[1,2,3],[4,5]]],[[[6,7]]]]

1 2 3 4                        123 4 5  [<=ネストの深さ]

-                              -
 (ここ)                         -
  -                              -      
    -                              -    
      - 1                            - 1
      - 2                            - 2
      - 3  singleton(return)         - 3
    -     ===================>     -    
      - 4                            - 4
      - 5                            - 5
 (ここ)                         -
  -                              -      
    -                              -    
      - 6                            - 6
      - 7                            - 7

同様に、2列と3列の間に入れるのはmap.map.singleton,...とつづく。
以上のようにして、各Diについて定義できるだけの(map^n).singletonを定義しておく。

圏の射は、上のようにして作ったflatten系の関数と、singleton系の関数(と、恒等写像)とする。

これで、圏の設定ができた。

モナドであること

このリストの圏にはモナド構造を入れることができる。
モナドは自己関手T、自然変換\eta、自然変換\muの組[tex:]で、ある性質を満たすもの。
Tは自己関手なので、T:D \to D
自然変換\etaは、\eta:I \to T (Iは恒等関手)。
自然変換\muは、\mu:T^2 \to T
満たすべき性質は、図式を使うので省略。http://www.ipsj.or.jp/07editj/promenade/4703.pdfの定義5を参照。
リストの圏に対しては、THaskellのmapとすればよい。
\eta,\muは自然変換なので、対象から射への写像となり、それぞれ、
\eta(D_n) = \mbox{singleton} :: D_n \to D_{n+1}
\mu(D_n) = \mbox{flatten} :: D_{n+2} \to D_{n+1}
とすればよい。これでモナドの定義を満たしている。
確認はめんどくさいだけで、コツコツやれば比較的簡単にできる。
これで、リストの圏で<\mbox{map},\eta,\mu>モナドになっていることがわかった。

めでたい。
と思ったが、やっぱりHaskellモナド則には見えてこない。
色々探して、Haskellモナド則に対応する圏論の概念は、Kleisli tripleというものらしい。
http://www.ipsj.or.jp/07editj/promenade/4703.pdfなどによると、Kleisli tripleからモナドを構成することができるらしい。
Listがモナドだと分かったから、モナド→Kleisli tripleの構成ができればいいはずだけど、モナドからKleisli tripleの構成がわからなかった。
ので、やってみる。

モナド→Kleisli triple

話を一般化して、Listに限らないモナドからKleisli tripleを構成する。

圏をDモナドを[tex:]とする。

モナドからでる可換性

準備として、モナドからでる可換性をまとめておく。(a)、(b)は自然変換から、(c)、(d)はモナドから出る可換性。
\phi:A\to B\quad(A,B\in D)とする。

  • (a) (T\phi)\cdot\mu_A = \mu_B\cdot(T^2\phi)
  • (b) (T\phi)\cdot\eta_A = \eta_B\cdot\phi
  • (c) \mu_A\cdot(T\mu_A) = \mu_A\cdot\mu_{TA}
  • (d) \mu_A\cdot\eta_{TA} = \mu_A\cdot(T\eta_A) = \mbox{id}_{TA}
Kleisli tripleの定義

http://www.ipsj.or.jp/07editj/promenade/4703.pdfから引用。文脈に合わせてちょっと改変。

D上の関手T:D\to D, 自然変換\eta:I\to D, 射\phi:X\to TY に対し、\phi^\ast : TX\to TYが以下の性質を満たすとき、[tex:]をKlisli triple という。

  1. \eta^\ast_X = \mbox{id}_{TX}
  2. \phi^\ast\cdot\eta_X = \phi
  3. \psi^\ast\cdot\phi^\ast = (\psi^\ast\cdot\phi)^\ast (\phi:X\to TY,\quad\phi:Y\to TZ)
モナドからKleisli tripleを構成する

Kleisli triple[tex:] はモナド[tex:]から、

  • T,\quad\etaはそのまま
  • \ast:\phi \vdash\to \mu_Y\cdot(T\phi), \quad \phi:X \to  TY, \quad (X,Y\in D)

とすればよい。

(1) \eta^\ast_X = \mbox{id}_{TX} ?

\eta^\ast_X = \mu_X\cdot(T\eta_X) (定義)
   = \mbox{id}_{TX}       (d)
OK!

(2) \phi^\ast\cdot\eta_X = \phi ?

\phi^\ast\cdot\eta_X = \mu_Y\cdot\underline{(T\phi)\cdot\eta_X}  (定義)
      = \underline{\mu_Y\cdot\eta_{TY}}\cdot\phi        (b)
      = \mbox{id}_{TY}\cdot\phi           (d)
      = \phi

OK!

(3) \psi^\ast\cdot\phi^\ast = (\psi^\ast\cdot\phi)^\ast ? (\phi:X\to TY,\quad\psi:Y\to TZ)

左辺を変形して右辺にする。

まず両辺を、\astの定義で書き直す。
左辺 = \mu_Z\cdot(T\psi)\cdot\mu_Y\cdot(T\phi)
右辺 = \mu_Z\cdot(T(\mu_Z\cdot(T\psi)\cdot\phi))

左辺を変形していく
左辺 = \mu_Z\cdot\underline{(T\psi)\cdot\mu_Y}\cdot(T\phi)

    = \underline{\mu_Z\cdot\mu_{TZ}}\cdot(T^2\psi)\cdot(T\phi)   (a)

    = \mu_Z\cdot\underline{(T\mu_Z)\cdot(T^2\psi)\cdot(T\phi)}   (c)

    = \mu_Z\cdot(T(\mu_Z\cdot(T\psi)\cdot\phi)) (関手のくくりだし)
   = 右辺
OK!

ということで

が確認できた。

めでたしめでたし。
しかし、これでモナドがわかった気にあまりなれないし、実際にHaskellでどういう場合にモナドを使えばいいのかもピンとこない。

もうすこし精進しないといけないなぁ。

*1:Haskellなら、D1::[Double]、D2::Double...