haskell - Isn't it redundant for Control.Lens.Setter to wrap types in functors? -


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 of lens package.
  • it rather memory inefficient build lots of tuples, rip them apart later.
  • the big one: functions take getters (like view) , setters (like over) 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 -> a equivalent forall r. (a -> r) -> s -> r. transformation continuation passing style far obvious. might able intuit transformation this: "a function of type s -> a promise given s can hand me a. should equivalent promise given function maps a r can hand me function maps s r also." maybe? maybe not. there might leap of faith involved here.

  • now define newtype const r = const r deriving functor. note const r a same r, mathematically , @ runtime.

  • now note type getter s = forall r. (a -> r) -> s -> r can rewritten type 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. note identity a same a, mathematically , @ runtime.

  • now note type setter s t b = (a -> identity b) -> s -> identity t still 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."