Lens パッケージの型の関係について

Lens パッケージの型の関係について

はじめに

たまに Lens を使うことがあって便利なんだけどよくわからないまま雰囲気で使っているところがあって、もう少し理解したいなという気がしていました。

なかなか難しくていまだに大半は理解できてないんですが Lens の型の図の意味が少し分かってきた気がするのでそれについて書きます。

使い方などについてはこの記事では触れません。

関係図

https://github.com/ekmett/lens/blob/b6a237453192f2b464f17e02929b57542a936b14/images/Hierarchy.png?raw=true

こういう図があってオブジェクト指向のクラス図のような感じなんですが、 Haskell にはクラスも継承もないのでこの矢印はどういう意味になっているのかよく分かってませんでした。

型の関係

Lens と Traversal を比べてみます。

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t

図だと Lens の方が下にあって Lens から Traversal に矢印が伸びています。

しかし上の型を見ても二つがどういう関係にあるのか自分にはすぐ理解できませんでした。

比較1

簡単な例から考えてみることにします。

type E1 a = forall f. Functor f => f a

type E2 a = forall f. Applicative f => f a

E1 は Functor 制約が付いていて、E2 には Applicative 制約が付いています。

http://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Applicative.html#g:1

Functor は Applicative のスーパークラスになっているので

  • Applicative であれば Functor でもある
  • Functor は Applicative ではない場合もある
  • E2 a の値は E1 a の値でもある
  • E1 a の値の数は E2 a の値の数より多い

のような感じのことが考えられると思います。

こう書くのが正しいかわかりませんが E2 aE1 a の部分集合になっていると思います。

$$ E1 \ a \supset E2 \ a $$

比較2

次に関数の引数に制約が付いている場合を考えてみます。

type E3 a b = forall f. Functor f => f a -> b

type E4 a b = forall f. Applicative f => f a -> b

E3 は Functor 制約の付いた引数を受け取る関数、E4 は Applicative 制約の付いた引数を受け取る関数です。

Functor の値の方が多いので E3 の方が入力となる値が多く、 E4 のほうが入力になる値が限定されていて関数が満たす必要がある要求が小さいというふうに考えられます。

この場合は比較1の例と逆に E3 a bE4 a b の部分集合になります。

$$ E3 \ a \ b \subset E4 \ a \ b $$

比較3

制約が付いていない引数が付いた場合です。

type E5 a b = forall f. Functor f => a -> f b

type E6 a b = forall f. Applicative f => a -> f b

この場合は E5 と E6 の入力になる値は同じだけあって、関数が満たす必要がある要求は変わりません。

関係を見る上ではこの引数は無視できるので比較1の場合と同じになります。

$$ E5 \ a \ b \supset E6 \ a \ b $$

比較4

Lens と Traversal の場合に戻ります。

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

type Traversal s t a b = forall f. Applicative f => (a -> f b) -> s -> f t

無視できる引数を消してみます。

type Lens_ t b = forall f. Functor f => f b -> f t

type Traversal_ t b = forall f. Applicative f => f b -> f t

引数と戻り値の型の両方に制約が付いているんですが、この場合は引数と同じ制約を付けた型を返す関数なので引数の方だけ見るということでいいと思います。

結局 Lens と Traversal の場合は比較2と同じように考えられると思います。

Lens は Traversal の部分集合になっているようです。

$$ Lens \ s \ t \ a \ b \subset Traversal \ s \ t \ a \ b $$

まとめ

矢印は部分集合の関係にあることを表していると思います。

Lens の図の上の方に行くほど関数の入力が限定されて関数は作りやすくなり、集合に含まれる関数は多くなります。

下の方に行くほど関数の入力になる値が多くなり、関数は作りにくくなり、集合に含まれる関数は少なくなります。

一番下にある Equality は他の型の全ての部分集合になっていて、Equality の値である id は他の全ての型として使ったり、合成することができます。