Avendia19
English

日記 (2021 年 7 月 7 日)

Haskell には lens というパッケージがあり、 オブジェクトの一部分を取り出したり書き換えたりする操作を抽象化したレンズと呼ばれる仕組みを提供している。 2019 年 4 月 25 日では、 このパッケージで定義されているレンズの型と、 素朴にゲッターとセッターを対にしたタプル型とが、 実は等価になることを述べた。 今回は、 この事実を圏論的視点から見る。

全体のオブジェクトの型を s で表し、 そのオブジェクトの中の取り扱いたい部分の型を a で表すことにすると、 ゲッターの型とセッターの型はそれぞれ s -> as -> a -> s と書けるのであった。 ここではこれを少し一般化して、 セッターを使うときに渡すセットする値の型が a と異なっていたり、 セッターで値をセットした結果得られる全体のオブジェクトの型が s と異なっていたりしても良いことにする。 この場合、 セッターに渡す値の型を a' と書き、 セッターが返す型を s' と書くことにすると、 セッターの型は s -> a' -> s' になる。 この設定のもと、 ゲッターとセッターの対の型は (s -> a, s -> a' -> s') とタプル型として実現される。 以降、 この型の値を素朴レンズと呼ぶことにする。

まずは、 素朴レンズを圏論的に定式化しよう。 これは単純に次のようにすれば良い。 なお、 後々の便宜のため、 上記の s, s', a, a' に対応する変数名としてそれぞれ 󰔄S, S, 󰔄A, A を用いる (プライム記号と上線の付け方に注意)。

定義 1.1

有限積をもつ圏 󰒚 をとる。 対象 󰔄S,S,󰔄A,A に対し、 2 つの射 g:󰔄S󰔄Ap:󰔄S×AS から成る組 (g,p)素朴レンズnaive lens と呼び、 (g,p):(󰔄S,S)󰔊(󰔄A,A) で表す。

素朴レンズを合成することで、 階層的により深い位置にある値を操作する新たな素朴レンズを得ることができた。 2019 年 4 月 25 日では、 この演算を @. という名前で定義している。 圏論的には、 この演算は次のように定式化できる。

定義 1.2

有限積をもつ圏 󰒚 をとる。 素朴レンズ (g,p):(󰔄S,S)󰔊(󰔄A,A), (g󰎘,p󰎘):(󰔄A,A)󰔊(B,󰔄B) に対し、 g󰎙 := 󰔄S󰔄ABgg󰎘 p󰎙 := 󰔄S×󰔄B󰔄S×󰔄S×󰔄B󰔄S×󰔄A×󰔄B󰔄S×ASdiag󰔄S×idid×g×idid×p󰎘p と定める。 なお、 図式中の diag󰔄S:󰔄S󰔄S×󰔄S は対角射である。 これによって得られる素朴レンズ (g󰎙,p󰎙):(󰔄S,S)󰔊(B,󰔄B)(g,p)(g󰎘,p󰎘)合成composition という。

この合成によって、 素朴レンズを射とする圏が定義できる。

定義 1.3

有限積をもつ圏 󰒚 をとる。 圏 NLens(󰒚) を、

によって定義する。

さて、 Haskell の lens パッケージにおけるレンズの型は、 上記のような素朴な (s -> a, s -> a' -> s') ではなく、 forall f. Functor f => (a -> f a') -> (s -> f s') という一見不思議な形になっている。 2019 年 4 月 25 日では、 一方の型の値からもう一方の型の値を作ることができ、 さらにその構成が互いに逆になっていることを観察して、 この 2 つの型が等価であることを述べた。 ここでは、 圏論的にこの 2 つの型の等価性を示そう。 そのためには、 後者の型を圏論的に定式化する必要がある。

forall f. Functor f => (a -> f a') -> (s -> f s') の値とはすなわち、 関手 f で添字付けられた (a -> f a') -> (s -> f s') という型の関数の族である。 これを素朴に捉えれば、 圏 󰒚 上の自己関手 F:󰒚󰒚 で添字付けられた写像 φF:Hom(󰔄A,FA)Hom(󰔄S,FS) の族 (φF)F が圏論的定式化であろうと思いつく。 しかし、 F に対して φF がバラバラに存在するというのは圏論的に不自然なので、 F に関して φF が自然であることを課すのは理に適っているだろう。 すなわち、 Hom(󰔄A,-A)Hom(󰔄S,-S) を関手 End(󰒚)Set と見なして、 自然変換 φ:Hom(󰔄A,-A)Hom(󰔄S,-S) を考えるということである。

残念ながら、 自然変換 φ:Hom(󰔄A,-A)Hom(󰔄S,-S) と素朴レンズ (g,p):(󰔄S,S)󰔊(󰔄A,A) は 1 対 1 に対応しない。 この原因は、 素朴レンズから上記の自然変換を作ろうとしたときに、 自然変換の添字として使われている F の関手性だけでは構造が足りないためである。 そこで、 自然変換の添字として自己関手に追加の構造を加えたものを考える必要性が出てくるが、 その構造として次で定義する強度という概念が適切であることが知られている。

定義 1.4

有限積をもつ圏 󰒚 上の自己関手 F:󰒚󰒚 をとる。 対象 M,X に対し、 全ての変数に関して自然な射 τMX:M×FXF(M×X) が定まっていて、 任意の対象 M,N,X に対し、 図式 (M×N)×FXF((M×N)×X)M×(N×FX)M×F(N×X)F(M×(N×X))τM×N,Xid×τNXτM,N×X FX1×FXF(1×X)τ1X が全て可換であるとする。 なお、 ラベルのない射は標準的な同型射を表している。 このとき、 τF に対する強度strength といい、 組 (F,τ)自己強関手strong endofunctor という。

強関手の間の射としては、 次のような強度と適合する自然変換を考える。

定義 1.5

有限積をもつ圏 󰒚 上の自己強関手 (F,τ),(G,σ):󰒚󰒚 および自然変換 θ:FG をとる。 任意の対象 M,X に対し、 M×FXM×GXF(M×X)G(M×X)id×θXθM×XτMXσMX が可換であるとき、 θ強自然変換strong natural transformation といい、 θ:(F,τ)(G,σ) で表す。

定義から強自然変換は合成に関して閉じていることがすぐに分かるので、 強関手と強自然変換は圏を成す。

定義 1.6

有限積をもつ圏 󰒚 をとる。 圏 StrEnd(󰒚) を、

によって定義する。

さて、 我々の目的は、 素朴レンズ (g,p):(󰔄S,S)󰔊(󰔄A,A) と 1 対 1 に対応するような自然変換 φ:Hom(󰔄A,-A)Hom(󰔄S,-S) を得ることであった。 Hom(󰔄A,-A)Hom(󰔄S,-S)End(󰒚) からの関手だと見るとこれはうまくいかないが、 StrEnd(󰒚) からの関手だと見ると実はうまくいく。 このように見なした自然変換 φ:Hom(󰔄A,-A)Hom(󰔄S,-S) を、 このエンコーディング方法を考案した人の名前を借りて、 van Laarhoven レンズと呼ぶことにしよう。

定義 1.7

有限積をもつ圏 󰒚 をとる。 対象 󰔄A,A に対し、 Hom(󰔄A,-A): StrEnd(󰒚) Set (F,τ) Hom(󰔄A,FA) とおく。 このとき、 対象 󰔄S,S,󰔄A,A に対し、 自然変換 φ:Hom(󰔄A,-A)Hom(󰔄S,-S)van Laarhoven レンズ— lens という。

定義 1.8

有限積をもつ圏 󰒚 をとる。 圏 LLens(󰒚) を、

によって定義する。

LLens(󰒚) の射としての向きと実体である自然変換としての向きが逆になっていることには注意すること。

では、 ここまでで定式化した 2 種類のレンズが等価であることを示そう。 すなわち、 有限積をもつ圏 󰒚 の任意の対象 󰔄S,S,󰔄A,A に対して、 全単射 HomLLens(󰒚)((󰔄S,S),(󰔄A,A))HomNLens(󰒚)((󰔄S,S),(󰔄A,A)) が存在することを示す。 直接この全単射を構成することもできるが、 van Laarhoven レンズの定義に出てくる Hom(󰔄A,-A):StrEnd(󰒚)Set という形の関手には特別な性質があるので、 それを介して証明することにする。

定理 1.9

カルテシアン閉圏 󰒚 をとる。 対象 󰔄A,A に対し、 関手 Hom(󰔄A,-A):StrEnd(󰒚)Set は表現可能である。

Hom(󰔄A,-A):StrEnd(󰒚)Set の表現対象を具体的に構成する。 まず、 E󰔄AA: 󰒚 󰒚 X (AX)×󰔄A とおく。 対象 M,X に対し、 積と冪の随伴の余単位を evAX:(AX)×AX で書くことにし、 射 idM×evAX:M×(AX)×AM×X を考える。 これを A に関して転置すれば (idM×evAX):M×(AX)A(M×X) が得られ、 さらに右から 󰔄A との積をとると、 ρMX󰔄AA:M×(AX)×󰔄A(A(M×X))×󰔄A が得られる。 すると、 これは MX の両方に関して自然であり、 さらに強度の公理を満たす。 したがって、 (E󰔄AA,ρ󰔄AA) は自己強関手である。

この (E󰔄AA,ρ󰔄AA) が実際に Hom(󰔄A,-A):StrEnd(󰒚)Set の表現対象であること、 すなわち HomStrEnd(󰒚)((E󰔄AA,ρ󰔄AA),-)Hom󰒚(󰔄A,-A) が成り立つことを以下に示す。

自己強関手 (F,τ) を固定する。 任意の強自然変換 θ:(E󰔄AA,ρ󰔄AA)(F,τ) に対し、 idA:AA の転置を jdA:1AA と書くことにして、 Φθ:=󰔄A1×󰔄A(AA)×󰔄AFAjdA×idθA とおくと、 これは写像 Φ: HomStrEnd(󰒚)((E󰔄AA,ρ󰔄AA),(F,τ)) Hom󰒚(󰔄A,FA) θ Φθ を定める。 逆に、 射 f:󰔄AFA および対象 X に対し、 (Ψf)X:=(AX)×󰔄A(AX)×FAF((AX)×A)FXid×fτAX,AFevAX とおくと、 これは X に関して自然なので、 自然変換 Ψf:E󰔄AAF が得られる。 さらにこれは強自然変換 Ψf:(E󰔄AA,ρ󰔄AA)(F,τ) にもなっていることが確かめられる。 これにより、 写像 Ψ: Hom󰒚(󰔄A,FA) HomStrEnd(󰒚)((E󰔄AA,ρ󰔄AA),(F,τ)) f Ψf が定まる。 ΦΨ は互いに逆になっており、 さらに自然でもあるので、 これより式 が得られた。

定理 1.10

カルテシアン閉圏 󰒚 をとる。 対象 󰔄S,S,󰔄A,A に対し、 全単射 HomLLens(󰒚)((󰔄S,S),(󰔄A,A))HomNLens(󰒚)((󰔄S,S),(󰔄A,A)) が存在する。

定理 1.9 と Yoneda の補題により、 HomLLens(󰒚)((󰔄S,S),(󰔄A,A)) = Hom[StrEnd(󰒚),Set](Hom(󰔄A,-A),Hom(󰔄S,-S)) Hom[StrEnd(󰒚),Set](Hom((E󰔄AA,ρ󰔄AA),-),Hom(󰔄S,-S)) Hom(󰔄S,E󰔄AAS)) Hom(󰔄S,(AS)×󰔄A) Hom(󰔄S,AS)×Hom(󰔄S,󰔄A) Hom(󰔄S×A,S)×Hom(󰔄S,󰔄A) = HomNLens(󰒚)((󰔄S,S),(󰔄A,A)) が成り立つので、 示された。

この射の間の全単射は圏同型を与えることも知られている。

定理 1.11

カルテシアン閉圏 󰒚 をとる。 圏同型 LLens(󰒚)NLens(󰒚) が成立する。

定理 1.10 の同型が合成を保つことを示せば良い。 単純に計算するだけなので、 ここでは詳細は省略する。

以上により、 ゲッターとセッターから成る素朴レンズと、 強関手で添字付けられた自然変換としての van Laarhoven レンズが、 互いに等価であることが示せた。 後者の自然変換としてのレンズは、 以前述べたように Haskell で用いられているレンズのエンコーディング方法である。 レンズにはプロ関手を用いた別のエンコーディング方法もあり、 これは PureScript のレンズライブラリで用いられている。 数学的には、 プロ関手を用いたエンコーディングの方が見通しが良く計算もしやすいという利点があり、 こちらの方がより深く研究されているようである。 詳細は、 Riley2 や Clarke et al1 などを参照すると良い。 余力があればこの日記シリーズでも触れたいと思う。

参考文献