日記 (2021 年 7 月 7 日)
Haskell には lens というパッケージがあり、 オブジェクトの一部分を取り出したり書き換えたりする操作を抽象化したレンズと呼ばれる仕組みを提供している。 このページでは、 このパッケージで定義されているレンズの型と、 素朴にゲッターとセッターを対にしたタプル型とが、 実は等価になることを述べた。 今回は、 この事実を圏論的視点から見る。
全体のオブジェクトの型を s
で表し、 そのオブジェクトの中の取り扱いたい部分の型を a
で表すことにすると、 ゲッターの型とセッターの型はそれぞれ s -> a
と s -> a -> s
と書けるのであった。
ここではこれを少し一般化して、 セッターを使うときに渡すセットする値の型が a
と異なっていたり、 セッターで値をセットした結果得られる全体のオブジェクトの型が s
と異なっていたりしても良いことにする。
この場合、 セッターに渡す値の型を a'
と書き、 セッターが返す型を s'
と書くことにすると、 セッターの型は s -> a' -> s'
になる。
この設定のもと、 ゲッターとセッターの対の型は (s -> a, s -> a' -> s')
とタプル型として実現される。
以降、 この型の値を素朴レンズと呼ぶことにする。
まずは、 素朴レンズを圏論的に定式化しよう。
これは単純に次のようにすれば良い。
なお、 上記の s
, s'
, a
, a'
に対応する変数名としてそれぞれ
有限積をもつ圏
素朴レンズを合成することで、 階層的により深い位置にある値を操作する新たな素朴レンズを得ることができた。
このページでは、 この演算を @.
という名前で定義している。
圏論的には、 この演算は次のように定式化できる。
有限積をもつ圏
この合成によって、 素朴レンズを射とする圏が定義できる。
有限積をもつ圏
の対象は、NLens ( ) の 2 つの対象の組 の全体とする。( S , S ) の 2 つの対象NLens ( ) の間の射は、 素朴レンズ( S , S ) , ( A , A ) の全体とする。 すなわち、( g , p ) : ( S , S ) → ( A , A ) である。Hom NLens ( ) ( ( S , S ) , ( A , A ) ) = Hom ( S , A ) × Hom ( S × A , S ) の射の合成は、 上記で定めた素朴レンズの合成とする。NLens ( )
によって定義する。
さて、 Haskell の lens パッケージにおけるレンズの型は、 上記のような素朴な (s -> a, s -> a' -> s')
ではなく、 forall f. Functor f => (a -> f a') -> (s -> f s')
という一見不思議な形になっている。
このページでは、 一方の型の値からもう一方の型の値を作ることができ、 さらにその構成が互いに逆になっていることを観察して、 この 2 つの型が等価であることを述べた。
ここでは、 圏論的にこの 2 つの型の等価性を示そう。
そのためには、 後者の型を圏論的に定式化する必要がある。
型 forall f. Functor f => (a -> f a') -> (s -> f s')
の値とはすなわち、 関手 f
で添字付けられた (a -> f a') -> (s -> f s')
という型の関数の族である。
これを素朴に捉えれば、 圏
残念ながら、 自然変換
有限積をもつ圏
強関手の間の射としては、 次のような強度と適合する自然変換を考える。
有限積をもつ圏
定義から強自然変換は合成に関して閉じていることがすぐに分かるので、 強関手と強自然変換は圏を成す。
有限積をもつ圏
の対象は、StrEnd ( ) 上の自己強関手 の全体とする。( F , σ ) の 2 つの対象StrEnd ( ) の間の射は、 強自然変換( F , σ ) , ( G , τ ) の全体とする。θ : ( F , σ ) ⇒ ( G , τ )
によって定義する。
さて、 我々の目的は、 素朴レンズ
有限積をもつ圏
有限積をもつ圏
の対象は、LLens ( ) の 2 つの対象の組 の全体とする。( S , S ) の 2 つの対象LLens ( ) の間の射は、 van Laarhoven レンズ( S , S ) , ( A , A ) の全体とする。 すなわち、φ : Hom ( A , - A ) ⇒ Hom ( S , - S ) である。Hom LLens ( ) ( ( S , S ) , ( A , A ) ) = Hom [ StrEnd ( ) , Set ] ( Hom ( A , - A ) , Hom ( S , - S ) ) の射の合成は、 通常の自然変換の合成を逆向きに行うものとする。LLens ( )
によって定義する。
では、 ここまでで定式化した 2 種類のレンズが等価であることを示そう。
すなわち、 有限積をもつ圏
カルテシアン閉圏
この
自己強関手
逆に、 射
カルテシアン閉圏
定理 1.9 と Yoneda の補題により、
この射の間の全単射は圏同型を与えることも知られている。
カルテシアン閉圏
定理 1.10 の全単射が合成を保つことを示せば良い。 単純に計算するだけなので、 ここでは詳細は省略する。
以上により、 ゲッターとセッターから成る素朴レンズと、 強関手で添字付けられた自然変換としての van Laarhoven レンズが、 互いに等価であることが示せた。 後者の自然変換としてのレンズは、 以前述べたように Haskell で用いられているレンズのエンコーディング方法である。 レンズにはプロ関手を用いた別のエンコーディング方法もあり、 これは Purecript のレンズライブラリで用いられている。 数学的には、 プロ関手を用いたエンコーディングの方が見通しが良く計算もしやすいという利点があり、 こちらの方がより深く研究されているようである。 次回は、 このプロ関手を用いたエンコーディングについて触れる。
参考文献
- B. Clarke et al (2020) 「Profunctor optics, a categorical update」 arXiv:2001.07488
- M. Riley (2018) 「Categories of optics」 arXiv:1809.00738