Lenses, often described as first class getters and setters, can help simplify code for manipulating nested data structures. In this post I’m going to look at how to map the most popular Haskell representation, van Laarhoven lenses, to OCaml.

### Lenses ala van Laarhoven

I won’t cover lenses in Haskell but a good starting point is this talk by Simon Peyton Jones. Following is the the basic definition of a lens, first proposed by Twan van Laarhoven:

```
type Lens a b = forall f. Functor f => (b -> f b) -> (a -> f a)
```

As we shall see, the curious thing is that this data type embeds
a *getter* for extracting values as well as a *setter* for
updating a value. This is possible by varying the choice of the `Functor`

on
the call site.

### Mapping to OCaml

The Haskell definition above is not directly translatable to OCaml due to the lack of higher-kinded polymorphism. That is, the following attempt does not quite work:

```
(* Invalid *)
type ('a,'b) lens = ('b -> 'b f ) -> ('a -> 'a f)
```

A general scheme for working around this limitation is to turn to the module
system. First we need a representation of Haskell *functors*. In Haskell, a
`Functor`

, is a type class with a function `map`

:

```
class Functor f where
fmap :: (a -> b) -> f a -> f b
```

In OCaml this can be represented using a module signature:

```
module type FUNCTOR = sig
type 'a t
val map : ('a -> 'b) -> 'a t -> 'b t
end
```

For a more comprehensive discussion on how to map type classes in Haskell to modules in OCaml, see this post.

The lens type itself can be achieved by another module type for wrapping
the type parameters along with a module functor for constructing the
the lens function given any concrete `FUNCTOR`

implementation:

```
module type LENS = sig
type a
type b
module Mk : functor (F : FUNCTOR) -> sig
val run : (b -> b F.t) -> a -> a F.t
end
end
```

In other words - a *lens* from `a`

to `b`

is a module that provides a
constructor (`Mk`

) for building another module that exposes a function `run`

defining the lens.

To bridge the gap between modules and types one can also create a type alias:

```
type ('a,'b) lens = (module LENS with type a = 'a and type b = 'b)
```

and a function for simplifying construction of lens values:

```
let mk_lens (lens : (module LENS with type a = 'a and type b = 'b)) : ('a, 'b) lens = lens
```

Before looking at how to actually use lenses for extracting or setting values, let’s consider an example instance. Say we have the following types:

```
type address = { street : string ; number : int; postcode : string }
type person = { name : string; age : int; address : address }
```

Below is a lens pointing to the `address`

property of a `person`

:

```
let address =
mk_lens (
module struct
type a = person
type b = address
module Mk (F : FUNCTOR) = struct
let run f x = F.map (fun address -> { x with address }) @@ f x.address
end
end
)
```

The interesting bit is the definition of `run`

which takes arguments `f`

and `x`

,
where:

```
val f : address -> address F.t
val x : person
```

Note that `F`

is an arbitrary `FUNCTOR`

. There’s really only one possible
(non-trivial) implementation of run given these constraints in general.
Definitions of lenses corresponding to properties is mostly boilerplate
and should rather be automated (for instance via *ppx deriving*).

### Modifying values

To see why this is useful at all, let’s look at how to apply lenses
for modifying or setting values. Lens libraries typically provide a function
`modify`

equivalent to the following signature:

```
val modify : ('a, 'b) lens -> ('b -> 'b) -> 'a -> 'a
```

Using the `address`

lens from above as an example, we have:

```
modify address : (address -> address) -> person -> person
```

For this purpose we ultimately need the `run`

function of a lens to be identical to:

```
let run f x = { x with address = f x.address }
```

This can be achieved by picking a functor that does not do anything besides applying the argument; The so called identity functor:

```
module IdFunctor : FUNCTOR with type 'a t = 'a = struct
type 'a t = 'a
let map f x = f x
end
```

To see why exactly this works out:

```
let run f x = F.map (fun address -> { x with address }) @@ f x.address =
(* Definition of map for IdFunctor *)
(fun address -> { x with address }) @@ f x.address
(* Beta reduction *)
{ x with address = f x.address }
```

Putting the pieces together, here’s the implementation of a function `modify`

:

```
let modify (type u)
(type v)
(lens : (module LENS with type a = u and type b = v))
(f : (v -> v))
(x : u) =
let module L = (val lens) in
let module R = L.Mk (IdFunctor) in
R.run f x
```

As an example, let’s define another lens referencing the `postcode`

property of an address:

```
let postcode =
mk_lens (
module struct
type a = address
type b = string
module Mk (F : FUNCTOR) = struct
let run f x = F.map (fun postcode -> { x with postcode }) @@ f x.postcode
end
end
)
```

Here’s how to use `modify`

to map over the postcode:

```
let address = { street = "Highstreet"; number = 42; postcode = "e1w" };;
modify postcode String.uppercase_ascii address;;
- : address = {street = "Highstreet"; number = 42; postcode = "E1W"}
```

For completeness one should also include a special case of `modify`

for replacing a value:

```
let set lens x = modify lens (fun _ -> x)
```

where

```
val set : ('a,'b) lens -> 'b -> 'a -> 'a
```

Here’s an example that *sets* the postcode of the address value above:

```
set postcode "XYZ" address;;
- : address = {street = "Highstreet"; number = 42; postcode = "XYZ"}
```

### Extracting values

Lenses can also be used for extracting or *viewing* the values
pointed to - we are aiming for a function with the following
signature:

```
val view : ('a, 'b) lens -> 'a -> 'b
```

Extracting values using lenses involves some cleverness in terms
of picking the right functor. For example, to get the address from a
person via the `address`

lens from above, we to tweak the run function.
Looking at its defintion again:

```
let run f x = F.map (fun address -> { x with address }) @@ f x.address
```

We need `run`

to evaluate to `x.address`

. The trick is to pick a functor that
ignores the function argument and returns a constant value:

```
module type TYPE = sig type t end
module ConstFunctor (T : TYPE) : FUNCTOR with type 'a t = T.t = struct
type 'a t = T.t
let map _ x = x
end
```

With `ConstFunctor`

at our disposal a function `view`

can be accomplished with:

```
let view (type u)
(type v)
(lens : (module LENS with type a = u and type b = v))
(x : u) =
let module L = (val lens) in
let module R = L.Mk (ConstFunctor (struct type t = v end)) in
R.run (fun x -> x) x
```

To look at how this adds up, simply expand the definition of `R.run`

.

Here’s an example of how to use `view`

:

```
view postcode { street = "Highstreet"; number = 42; postcode = "e1w" };;
- : string = "e1w"
```

### Composing lenses

There would be little point in defining lenses if it weren’t for the ability to compose them. We can either define composition as a module functor or via a function inlining the module construction. Here’s the latter version:

```
let compose (type u)
(type v)
(type x)
(l1 : (module LENS with type a = v and type b = x))
(l2 : (module LENS with type a = u and type b = v)) =
mk_lens (
module struct
type a = u
type b = x
module L1 = (val l1)
module L2 = (val l2)
module Mk (F : FUNCTOR) = struct
module R1 = L1.Mk (F)
module R2 = L2.Mk (F)
let run f x = R2.run (R1.run f) x
end
end
)
```

To fully appreciate the power of `compose`

consider the infix version:

```
val (//) : ('a, 'b) lens -> ('c, 'd) lens -> ('a, 'c) lens
```

As an example, say we have a value `person`

:

```
let mary =
{
name = "Mary";
age = 33;
address = { street = "Highstreet"; number = 42 ; postcode = "e1w"}
}
```

Viewing or updating the postcode code of `mary`

is straigt forward:

```
view (address // postcode) mary;;
:- "e1w"
update (address // postcode) String.uppercase_ascii mary;;
:- { name = "Mary"; age = 33; address =
{ street = "Highstreet"; number = 42 ; postcode = "E1W"} }
set (address // postcode) "XYZ" mary;;
:- { name = "Mary"; age = 33; address =
{ street = "Highstreet"; number = 42 ; postcode = "XYZ"} }
```