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 :: s → a
	, set :: s → b → t
	}
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 ⇒ (a → f b) → (s → f 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 , for any functor and object in , we have the following natural isomorphism.
In haskell, with , we would write
	Functor f ⇒ Reader a ~> f ≅ f a
where
	type Reader a b = a → b
	type f ~> g = ∀ x. f x → g x
If we take for some object in , then we have
In haskell for
	Reader a ~> Reader b ≅ b → a
Now we consider , the category of endofunctors on .
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 ~> g ≅ s → (a, b → t) 
		   ≅ (s → a, b → t) 
		   ≅ Lens s t a b
and for all functors f,
    (g ~> f) → (h ~> f) ≅ (a → f b) → (s → f t)
For now, let’s focus on choosing g such that
	∀ f. Functor f ⇒ g ~> f ≅ ∀ f. Functor f ⇒ a → f 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₁.
	t → R₁ f
	≅ t → f b
	≅ t → Reader b ~> f
	≅ ∀ x. t → (b → x) → f x
	≅ ∀ x. (t, b → x) → f x
Let L₁ t x = (t, b → x).
Then
	t → R₁ f
	≅ ∀ x. (t, b → x) → f x
	≅ L₁ t ~> 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
	≅ L₁ a ~> f
	≅ a → R₁ f
	≅ a → f 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
	≅ L₂ s ~> L₁ a
	≅ s → R₂ (L₁ a)
	≅ s → L₁ a t
	≅ s → (a, b → t)
	≅ Lens s t a b
Therefore,
	∀ f. Functor f ⇒ (a → f b) → (s → f t) ≅ Lens s t a b