i'm watching control.lens introduction video.
makes me wonder why needed setter type wrap things in functors.
it's (roughly) defined this:
type control.lens.setter s t b = (functor f) => (a -> f a) -> s -> f t let's have data called point that's defined this:
data point = point { _x :: int, _y :: int } deriving show then can write own xlens this:
type mysetter s t b = (a -> b) -> s -> t xlens :: mysetter point point int int xlens f p = p { _x = f (_x p) } and can use this:
p = point 100 200 xlens (+1) p -- results in point { _x = 101, _y = 200 } by using control.lens, same effect achieved by:
over x (+1) p where following stands:
x :: functor f => (int -> f int) -> point -> f point on :: setter point point int int -> (int -> int) -> point -> point so question is, since same effect can achieved in simpler manner, why control.lens wraps things in functors? looks redundant me, since xlens same control.lens's over x.
just record, can chain lenses in same manner:
data atom = atom { _element :: string, _pos :: point } deriving show poslens :: mysetter atom atom point point poslens f = { _pos = f (_pos a) } = atom "oxygen" p (poslens . xlens) :: (int -> int) -> atom -> atom (poslens . xlens) (+1) -- results in atom "oxygen" (point 101 200)
this wonderful question , require little bit of unpacking.
i want gently correct on 1 point right off bat: type of setter in lens package of recent versions is
type setter s t b = (a -> identity b) -> s -> identity t no functor in sight ... yet.
that not invalidate question however. why isn't type simply
type setter s t b = (a -> b) -> s -> t for first have talk lens.
lens
a lens type allows perform both getter , setter operation. these 2 combined form 1 beautiful functional reference.
a simple pick lens type is:
type getter s = s -> type setter s t b = (a -> b) -> s -> t type lens s t b = (getter s a, setter s t b) this type unsatisfying.
- it fails compose
., perhaps single best selling point oflenspackage. - it rather memory inefficient build lots of tuples, rip them apart later.
- the big one: functions take getters (like
view) , setters (likeover) cannot take lenses because types different.
without last problem solved why bother writing library? hate users have think in uml hierarchy of optics, adjusting function calls each time move or down.
the question of moment then: there type can write down lens such automatically both getter , setter? , have transform types of getter , setter.
getter
first note
s -> aequivalentforall r. (a -> r) -> s -> r. transformation continuation passing style far obvious. might able intuit transformation this: "a function of types -> apromise givenscan hand mea. should equivalent promise given function mapsarcan hand me function mapssralso." maybe? maybe not. there might leap of faith involved here.now define
newtype const r = const r deriving functor. noteconst r asamer, mathematically , @ runtime.now note
type getter s = forall r. (a -> r) -> s -> rcan rewrittentype getter s t b = forall r. (a -> const r b) -> s -> const r t. though introduced new type variables , mental anguish ourselves type still mathematically identical started out (s -> a).
setter
define
newtype identity = identity a. noteidentity asamea, mathematically , @ runtime.now note
type setter s t b = (a -> identity b) -> s -> identity tstill identical type started out with.
all together
with paperwork out of way, can unify setters , getters 1 single lens type?
type setter s t b = (a -> identity b) -> s -> identity t type getter s t b = forall r. (a -> const r b) -> s -> const r t well haskell , can abstract out choice of identity or const quantified variable. the lens wiki says, const , identity have in common each functor. choose sort of point of unification these types:
type lens s t b = forall f. functor f => (a -> f b) -> s -> f t (there other reasons choose functor too, such prove laws of functional references using free theorems. handwave little bit here time.) forall f forall r. above – lets consumers of type choose how fill variable in. fill in identity , setter. fill in const a , getter. choosing small , careful transformations along way able arrive @ point.
caveats
it might important note derivation not original motivation lens package. derivation wiki page states explains, can start interesting behavior of (.) functions , tease out optics there. think path carved out little better @ explaining question posed, big question had starting out too. want refer lens on tea, provides yet another derivation.
i think these multiple derivations thing , kind of dipstick healthiness of lens design. able arrive @ same elegant solution different angles means abstraction robust , well-supported different intuitions , mathematics.
i lied little bit type of setter in recent lens. it's actually
type setter s t b = forall f. settable f => (a -> f b) -> s -> t b this example of abstracting higher-order type in optical types provide library user better experience. f instantiated identity, there instance settable identity. every , might want pass setters backwards function, fixes f backwards identity. can categorize paragraph "more information lens wanted know."