Lens
ややこいので以下のように呼び分けよう
Lens
そもそもある概念
具体的な型
この辺が良いらしい
自分で定義した型に対してsetterやgetterなどの便利関数を自動生成する
TSではclassを作って、そのインスタンスに対して、データを更新したりgetしたりするが、それと同じようなことを関数型の世界でも行う
関数合成などのデザインもきれい
ネストがあるタプルやレコードの操作時にありがたくなる
逆にネストがなければそんなにありがたくない
tutorial
https://gyazo.com/884987b5a329720c502a8a9b1097f648
Lensを使って生成したgetterやsetterのことを「lenses」と呼ぶっぽいmrsekut.icon まあ単数形でもいいけど、わかり易さのために複数形にしてるmrsekut.icon
「Lens」と呼んで指す対象が多すぎるmrsekut.icon
package名
生成されたgetter/setter
特定の1つの関数
基本的にはこれさえ抑えておけばできる
makeLens or makeFields
fmap
多くの場合makeFieldsを使えばいい?mrsekut.icon
普通に.
アクセスする
view
over
set
over :: ASetter s t a b -> (a -> b) -> s -> t
これはsetter
view :: MonadReader s m => Getting a s a -> m a
これはgetter
^.というエイリアスがある
例
code:hs
data Atom = Atom { _element :: String, _point :: Point } deriving (Show)
data Point = Point { _x :: Double, _y :: Double } deriving (Show)
$(makeLenses ''Atom)
$(makeLenses ''Point)
-- use
let atom = Atom { _element = "C", _point = Point { _x = 1.0, _y = 2.0 } }
view (point . x) atom
1.0
ghci> atom^.point.x
1.0
ghci> atom^.point^.x
1.0
over (point . x) (+1) atom
Atom {_element = "C", _point = Point {_x = 2.0, _y = 2.0}}
以下は全て同じ意味
code:hs
view (point . x) atom
atom^.point.x
atom^.point^.x
.^はviewのエイリアスなので、1行目と2行目がおなじになるのはわかる
3行目は、統一感のためにあるのだろうか
普通はどれを使うんだろうmrsekut.icon
2行目かな
関数合成
Lens'型で単純化して見るとわかりやすいmrsekut.icon
型の前後は、普通の関数合成.と同じ感じになっっているmrsekut.icon
code:hs
point :: Lens' Atom Point
x :: Lens' Point Double
point . x :: Lens' Atom Double
Lens Laws
getterとsetterが以下の3つを満たす
set s (get s) == s
get (set s v) == v
get (set (set s v1) v2) == v2
フィールド系
makeLenses '' Hoge
setter
+~,-~,*~
フィールドが数値の場合に使える
+~10なら元の値に10を加算する
code:例.hs
{-# LANGUAGE TemplateHaskell #-} data Par = Par {
_foo :: Int,
_chi :: Chi
} deriving (Show, Eq)
data Chi = Chi {
_piyo :: String
} deriving (Show, Eq)
makeLenses ''Par
let par = (Par 1 (Chi "child"))
-- getter
_foo par -- 普通のアクセス
par^.foo -- Lensでのアクセス
par^.chi^.piyo -- チェーンもできる
("Piyo", par)^._2.foo -- タプル系の関数と組み合わせる
-- setter
par&foo.~3 -- 3に変更
par&foo+~10 -- 10加算する
makeFieldsを使う
mkLabelというのもあるらしい
タプル系
(^.) :: s -> Getting a s a -> a
演算子
view関数のエイリアス
_1: 1番目を取得
code:hs
(1, ('a', 'b', ("Hoge", "Piyo")), 2)^. _2 . _3 . _1
関数合成(.)をOOPのmethodに見立てているの面白いmrsekut.icon
(.~) :: ASetter s t a b -> b -> s -> t
値の変更
set関数のエイリアス
code:hs
_1._2.~True $ ((1,2),3) -- ((1,True),3)
(&) :: a -> (a -> b) -> b
code:hs
((1,2),3)&_1._2 .~ True -- ((1,True),3)
値の変更
to
ネストの深い部分に関数を適用した結果を得る
code:hs
("hoge", ("a", "nya"))^._2._2.to length -- 3
以下の違いってなに
code:hs
$(makeLenses ''Atom)
makeLenses ''Atom
$(..)って何の意味がある?順序とか?
Lensが型
実装が複数ある
何でもできるが、読み解くのが難しい
コンパクト
data-lensというパッケージもあるが、それは古いやつ
Lens内の型
Equality
Iso
tutorial
0個以上の任意の数の部分データに注目する
Lens型のgetter/setterであるview/overが、toListOf/overに変わった感じ table:aa
Lens型 Traversal型
getter view toListOf
setter over over
toListOfのエイリアスに^..
Getter
Setter
Review
圏論
Lensの実装が気になる
実装読んでないけどけっこう難解?らしいmrsekut.icon
後半
参考
概要がわかりやすい
Lensに関する大量の資料集
template haskellを使ってるので若干嫌われてる?