haskellのlensの使い方 (基本)
ekmett先生のlensに関して
Lens'
は便利に使えはするけど、Prism
,Iso
,Traversal
,Fold
みたいなのは触ったことがなかったので調べた。
Lensの利用
import Control.Lens
して
>>> ("hello",("world","!!!")) ^. _2 . _1
"world"
>>> ("hello",("world","!!!")) & _2 . _1 .~ 42
("hello",(42,"!!!"))
>>> ("hello",("world","!!!")) & _2 . _1 %~ map toUpper
("hello",("WORLD","!!!"))
みたいに使う。優先順位は以下のようになっている:
>>> ("hello",("world","!!!")) ^. (_2 . _1)
>>> ("hello",("world","!!!")) & ((_2 . _1) .~ 42)
>>> ("hello",("world","!!!")) & ((_2 . _1) %~ map toUpper)
同じ動きをする非演算子もある:
>>> view (_2 . _1) ("hello",("world","!!!"))
"world"
>>> set (_2 . _1) 42 ("hello",("world","!!!"))
("hello",(42,"!!!"))
>>> over (_2 . _1) (map toUpper) ("hello",("world","!!!"))
("hello",("WORLD","!!!"))
また、
x & f = f x
>>> [0..] & tail & take 4 & product & show
"24"
である。関数合成が向きのせいで苦手な人は(lensとは無関係に)常用するとよいと思う。
Lensを作る
この_1
,_2
のようなものを作るには大抵
{-# LANGUAGE TemplateHaskell #-}
data Foo a = Foo { _bar :: Int, _baz :: Int, _quux :: a }
makeLenses ''Foo
とする。この場合
bar, baz :: Lens' (Foo a) Int
quux :: Lens (Foo a) (Foo b) a b
のように定義され
>>> Foo 3 4 "foo" & baz %~ succ
Foo {_bar = 3, _baz = 5, _quux = "foo"}
>>> Foo 3 4 "foo" & quux .~ [2,3]
Foo {_bar = 3, _baz = 4, _quux = [2,3]}
のように使える。Lens' s a = Lens s s a a
である。
また
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
を使って
bar' :: Lens' (Foo a) Int
bar' = lens _bar (\ s b -> s { _bar = b })
のように、自分で定義を与えて作ることもできる。
Lensっぽいもの
取り出すだけでいい/入れるだけでいいものも同じように扱うことができる。例えばlistの長さの取得や標準出力への出力は
>>> "hoge" ^. to length
4
>>> let _print = sets (\ f x -> do { a <- f <$> x ; print a })
>>> return () & _print .~ "hello"
"hello" -- *side effect*
のように作ることができる。それぞれGetter
,Setter
という型であり、以下の関数で作られる:
to :: (s -> a) -> Getter s a
sets :: ((a -> b) -> s -> t) -> Setter s t a b
mapped :: Functor f => Setter (f a) (f b) a b
結論だけ言うと、Getter
は関数、Setter
は関手に、lensのinterfaceを与えたものです。
様々なlens類
最後に全体図を見てみよう。
Lens
はGetter
かつSetter
であるものの中で最も制約が弱いもの、なにか上限のようなものだと分かる。つまりview
,set
,over
などはGetter
,Setter
のmethodだったということです。
問題ないレベルではあるが補足: 図はところどころhaddockと食い違い(見易さのために手が入ってたり、単に古かったり)がある。この記事でも図にならって見易く型を書いています。
続く Haskellのlensの使い方 (詳しめ)
長くなったので分割しました。
参考
- https://hackage.haskell.org/package/lens
- https://github.com/ekmett/lens
- Lens で Haskell をもっと格好良く! for 2013/3/31 ekmett 勉強会 ちゅーん
- 基本から
Lens
とTraversable
の関係
- 基本から
- Lensで行こう! - みょんさんの。
- 基本から
Lens
とGetting
,Setting
の関係
- 基本から
- Lensで行こう!(2):Isoへの拡張 - みょんさんの。
Prism
やLens
記述時のlensのversionは4.6.0.1でした。