Lance Myers


Van Laarhoven Lenses

A lens is basically just a getter and setter, so one might expect something like the implementation below.

data Lens a b = Lens
{ get :: a -> b
, set :: (b -> b) -> (a -> a)
}


data Pair a b = Pair a b

_1 :: Lens (Pair a b) a
_1 = Lens get set
where
get (Pair x _) = x
set f (Pair x y) = Pair (f x) y

_2 :: Lens (Pair a b) b
_2 = Lens get set
where
get (Pair _ y) = y
set f (Pair x y) = Pair x (f y)

Unfortunately, it is not that simple. We would like lenses to be more polymorphic than that, so we introduce two more type parameters.

data Lens s t a b = Lens 
{ get :: sa
, set :: sbt
}

Here s is the type of the overall structure, a the type of the subpart being focussed on, b the new type of the subpart after being set, and t the type of the whole structure after being set. While this is more flexible, it still does not compose nicely. If we want a lens that focuses on x_2 in Pair x_1 (Pair x_2 x_3), we want to just write _1 . _2. In the lens library, lenses are actually implemented as

type Lens' s t a b = forall f. Functor f(af b)(sf t)

This is called a Van Laarhoven lens and contains the same data as the previous definition, just a bit obfuscated.

To show that this is isomorphic, we will need some basic category theory. The Yoneda lemma says that in a category CC, for any functor F:CSetF : C \to \mathsf{Set} and object aa in CC, we have the following natural isomorphism.

Nat(C(a,),F)Fa\mathsf{Nat}(C(a, -), F) \cong F a

In haskell, with C=SetC = \mathsf{Set}, we would write

	Functor fReader a ~> ff a

where

	type Reader a b = ab
type f ~> g =x. f xg x

If we take F=C(b,)F = C(b, -) for some object bb in CC, then we have

Nat(C(a,),C(b,))C(b,a)\mathsf{Nat}(C(a, -), C(b, -)) \cong C(b, a)

In haskell for C=SetC = \mathsf{Set}

	Reader a ~> Reader bba

Now we consider C=[Set,Set]C = [\mathsf{Set}, \mathsf{Set}], the category of endofunctors on Set\mathsf{Set}.

Nat([Set,Set](G,),[Set,Set(H,)])[Set,Set](H,G) \mathsf{Nat}([\mathsf{Set}, \mathsf{Set}](G, -), [\mathsf{Set}, \mathsf{Set}(H, -)] ) \cong [ \mathsf{Set}, \mathsf{Set} ](H, G)

In haskell, this is written

f. Functor f(g ~> f)(h ~> f)h ~> g

Notice that we know have a quantification over all functors, just like in the definition of Van Laarhoven lenses. Now we just have to choose the right functors for g and h. We would like to have

	h ~> gs(a, bt) 
(sa, bt)
Lens s t a b

and for all functors f,

    (g ~> f)(h ~> f)(af b)(sf t)

For now, let’s focus on choosing g such that

f. Functor fg ~> f ≅ ∀ f. Functor faf b

We have an isomorphism between hom-sets, so we should look for adjoint functors. Lets rewrite the right hand side as a → R₁ f where R₁ is the functor R₁ f = f b. We need to find L₁ left adjoint to R₁.

	tRf
tf b
tReader b ~> f
≅ ∀ x. t(bx)f x
≅ ∀ x. (t, bx)f x

Let L₁ t x = (t, b → x). Then

	tRf
≅ ∀ x. (t, bx)f x
Lt ~> f

which shows that L₁ is left adjoint to R₁. Recall we want g ~> f ≅ a → f b, so we choose g = L₁ a.

	  g ~> f
La ~> f
aRf
af b

Similarly, define R₂ f = f t, L₂ x y = (x, s -> y), and h = L₂ s to get h ~> f ≅ s → f t. Now, let’s go back to the right hand side.

	  h ~> g
Ls ~> La
sR(La)
sLa t
s(a, bt)
Lens s t a b

Therefore,

f. Functor f(af b)(sf t)Lens s t a b