wiki

Lens

also: lenses, functional reference, optic

A first-class pair of a getter and a setter for one part of a structure, with laws that make them agree: you get back what you set, setting what you got changes nothing, and a second set overwrites the first. Lenses compose, so a path through nested records is one value.

A record update in OCaml is shallow. Changing the city inside the address inside a person means rebuilding every record on the way down by hand. A lens packages one step of the way down, and composing two lenses gives the path through both.

A lens as a record of two functions, and composition.

type ('s, 'a) lens = { get : 's -> 'a; set : 'a -> 's -> 's }
let ( |-- ) l1 l2 =
{ get = (fun s -> l2.get (l1.get s));
set = (fun a s -> l1.set (l2.set a (l1.get s)) s) }
type address = { city : string; street : string }
type person = { name : string; address : address }
let address = { get = (fun p -> p.address); set = (fun a p -> { p with address = a }) }
let city = { get = (fun a -> a.city); set = (fun c a -> { a with city = c }) }

With ada living on St James's Sq, London. Compiled with ocamlopt 5.5.1.

(address |-- city).get ada -> "London"
((address |-- city).set "Paris" ada).address.city -> "Paris"
((address |-- city).set "Paris" ada).address.street -> "St James's Sq"

The laws are what make a lens more than two functions that happen to share a type: get (set a s) = a, set (get s) s = s, and set b (set a s) = set b s. They are what let a lens be used without reading its definition.

Haskell's lens library encodes a lens as a function polymorphic over a functor, forall f. Functor f => (a -> f a) -> s -> f s, which makes composition ordinary function composition. That needs higher-kinded polymorphism, which OCaml has only through functors, so a record of functions is the usual OCaml representation.

see also

referenced by

further reading

  • J. N. Foster, M. B. Greenwald, J. T. Moore, B. C. Pierce, A. Schmitt, “Combinators for bidirectional tree transformations: a linguistic approach to the view-update problem”, ACM TOPLAS 29 (2007).
  • T. van Laarhoven, “CPS based functional references” (2009).
  • M. Pickering, J. Gibbons, N. Wu, “Profunctor optics: modular data accessors”, The Art, Science, and Engineering of Programming 1 (2017).