generated at
Lens型

定義 ref
hs
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
s , t が全体の型
a , b が部分の型
全体の型を変更する可能性を含めて一般化しているので4つも型引数が必要になってるmrsekut
その可能性を排除すると以下のようなシンプル版を定義できる ref
hs
type Lens' s a = Lens s s a a
s が全体の型
e.g. (x,y)
a が部分の型
e.g. (x,y) に対する x



この型の表す意味と、この定義の意味
Lensという概念はそもそも、getterとsetterを一緒にしたもの
getterは「あるデータ」から「その一部」を取ってくる
setterは「その一部」を変化させて、新しい「あるデータ構造」を得る
この2つを同時に定義したものがLens型で、composabilyなどの良い性質を備えている
頭に思い浮かべるときは、後述の1段階目Lens型#64c65da21982700000ad84f9がパッと思い出せれば十分な気がするmrsekut



上記のような定義になることを理解するためには順を追っていく必要がある
その解説が、Lenses from Scratchに書いてるmrsekut
その内容を雑に書くと
1段階目
直観的な定義
getterとsetterを組にして定義する
a が全体の型、 b が部分の型
(>-) はLens同士を組み合わせる関数
lv1.hs
data Lens a b = Lens { get :: a -> b -- view , set :: b -> a -> a -- over } (>-) :: Lens a b -> Lens b c -> Lens a c la >- lb = Lens (get lb . get la) $ \part whole -> set la (set lb part (get la whole)) whole
2段階目
Store型などの概念を導入する
(>-) は割とやってることをそのまま書き下した感じになっている
lb2.hs
type Lens a b = a -> Store b a data Store b a = Store b (b -> a) (>-) :: Lens a b -> Lens b c -> Lens a c (la >- lb) a = let Store partB holeBA = la a Store partC holeCB = lb partB holeCA = holeBA . holeCB in Store partC holeCA
3段階目
上記ではStore型に依存してしまっているのでより抽象的な構造にする
以下のようにすることでFunctorであれば何でも良い、というようになった
Coalgebraを使うことで、合成もただの関数合成 (.) と同じになっている
lv3.hs
type Lens a b = Functor f => Coalg f b -> Coalg f a type Coalg f x = x -> f x (>-) :: Lens a b -> Lens b c -> Lens a c (>-) = (.)
これで上述の Lens' s a が完成している
4段階目
Lensを通して型を変換できるように、より一般的な定義に変更
lv4.hs
type Lens s t a b = Functor f => (a -> f b) -> (s -> f t) (>-) :: Lens s t a b -> Lens a b c d -> Lens s t c d (>-) = (.)









なんでこの1つの型で、getter/setterを表現できるのかの解説
functorに Identity を適用すればsetterになり、 Const b を適用すればgetterになる
すごすぎん?mrsekut