Lenses, also known as functional references, are a powerful way of looking at, constructing, and using functions on complex data types. They're also, unfortunately, a very new and complex subject making them challenging to learn. This tutorial intends to help lay out the basics of lensing. > I'm here assuming that you're familiar with moderate complexity Haskell. Truly, understanding the use of lenses isn't terribly difficult, but the phrasing, type errors, and use of Template Haskell can be confusing. I suggest below that if you do not recognize a snippet of Haskell entirely, try to read through it. Again, the lens concept isn't challenging, even if it looks that way at first The first complexity of lensing is that there are a variety of libraries offering "functional references" or "lenses", some of which are compatible and some of which aren't. These include `data-accessor`, `fclabels`, `lenses`, `data-lens`, and `lens`. The newest, largest, and most active library is `lens` offering the [`Control.Lens`](http://hackage.haskell.org/package/lens) module and the remainder of this article uses it. ``` haskell import Control.Lens ``` Feel free to take a look at the [Haddock documentation](http://hackage.haskell.org/package/lens), but beware it's quite dense, terse, and challenging. Lenses are a simple concept, but also very general. ## What is a lens anyhow? At its simplest, a lens is a *value* representing maps between a complex type and one of its constituents. This map works both ways—we can get or "access" the constituent *and* set or "mutate" it. For this reason, you can think of lenses as Haskell's "getters" and "setters", but we shall see that they are far more powerful. ``` haskell data Arc = Arc { _degree :: Int, _minute :: Int, _second :: Int } data Location = Location { _latitude :: Arc, _longitude :: Arc } -- This is a TH splice, it just creates some functions for us automatically based on the record functions in 'Location'. We'll describe them in more detail below. $(makeLenses ''Location) ``` Here we generate some lenses automatically for a record type `Location` using Template Haskell. Lenses are easy to write on your own, but we'll treat them as black boxes for a while. > The underscores in the record names `_degree`, `_minute`, etc. are a `Control.Lens` convention for generating TH. The result of this TH splice is the creation of two lenses, one corresponding to each field of the record. ``` haskell latitude :: Lens' Location Arc longitude :: Lens' Location Arc ``` > If you're following along in GHC, though, you'll get a bit of a surprise already. The inferred types of these lenses are quite exotic, like `latitude :: Functor f => (Arc -> f Arc) -> Location -> f Location`, betraying that `Lens'` is simply a strange `type` synonym. We'll understand this in much more depth later, but at first it's required to just remember the simpler type `Lens'`. We can use lenses as both getters and setters on `Location` types. ``` haskell getLatitude :: Location -> Arc getLatitude = view latitude setLatitude :: Arc -> Location -> Location setLatitude = set latitude ``` Which is so simple it almost makes us wonder why we ever bothered with the whole lens concept! After all, we can already write getters and setters using record syntax. ``` haskell getLatitudeR :: Location -> Arc getLatitudeR (Location { _latitude = lat }) = lat setLatitudeR :: Arc -> Location -> Location setLatitudeR lat loc = loc { _latitude = lat } ``` so all we've bought so far with lenses is the ability to wrap these two functions up into a single value. More power is to come, but this intuition is a great first step. In fact, it's the second way we'll see to build lenses, using the function `lens :: (c -> a) -> (c -> a -> c) -> Lens' c a` which takes a getter and setter and combines them into a lens! Using `lens` we can see how getters and setters turn into lenses and even note a law of lenses ``` haskell lens getLatitudeR (flip setLatitudeR) === -- we can replace the getters and setters with their lens versions lens getLatitude (flip setLatitude) === -- which have these definitions lens (view latitude) (flip $ set latitude) === -- which is identical to latitude -- OR, for all lenses, l l == lens (view l) (flip $ set l) ``` ## First joys of abstraction So what exactly do we buy, wrapping "getters" and "setters" up together? Well, for one, we can forgo record syntax (for better or worse) and export just the lenses instead of the record functions if we like. For another, we can have other kinds of combinators to operate on these lenses for affecting the "focal" record values. For instance, modification is immediately a combinator, `over` (and this is built in to the library itself) ``` haskell modifyLatitude :: (Arc -> Arc) -> (Location -> Location) modifyLatitude f = latitude `over` f -- which wraps the motif modifyLatitude :: (Arc -> Arc) -> (Location -> Location) modifyLatitude f lat = setLatitude (f $ getLatitude lat) ``` So, `over` allows us lift a function between the getter and the setter, to create a function which modifies just a tiny part of the greater whole. Really, `over` is nothing special—we've trivially built it from `getLatitude` and `setLatitude`, but you can begin to see the difference in thought. All of these various update/accessor functions have been rolled into a single value, *We can thus think of a lens as *focusing * in on a smaller part of a larger object.* That intution is powerful. ## Building telescopes So now that we have a basic understanding of lenses, let's build some more. ``` haskell $(makeLenses ''Arc) getDegreeOfLat :: Location -> Int getDegreeOfLat = view degree . view latitude setDegreeOfLat :: Int -> Location -> Location setDegreeOfLat = over latitude . set degree ``` Perfect! We can compose our getter and setter functions to dive more deeply. We could even combine these deeper, "more focused" lenses to form a new lens. ``` haskell degreeOfLat'Manually :: Lens' Location Int degreeOfLat'Manually = lens getDegreeOfLat (flip setDegreeOfLat) ``` But this is getting a little out of control. Haskell is all about having mind-sized chunks of computational value which we combine meaningfully. Is there a way to directly combine lenses? Yes, and the method may come as a surprise ``` haskell degreeOfLat = latitude . degree -- well, that was easy! ``` Lenses, with their weird underlying type involving `forall`s and functions of `Functor`s compose... much like functions do! The only difference is you read the composition right-to-left instead of the usual left-to-right function chaining. This can be a little confusing for a functional programmer, but if you squint it looks a lot like nested property referencing on an object in an OO language. > As an aside, while all of the lens libraries appreciate and allow for easy composition of lenses, only certain representations can be combined using `(.)` and it was quite a breakthrough to discover this. The details will be fleshed out later. You might think of these as placing two (real world) lenses in series—together they refine the optics to focus more and more deeply into the subject. ``` haskell (.) :: Lens' a b -> Lens' b c -> Lens' a c ``` ## Other kinds of optics If lens composition gives us telescopes, can we build other kinds of optical machinery? Let's look at other basic ways of composing Haskell types. Perhaps the two *most essential* methods are pairs and eithers, i.e. `(,)` and `Either`. Lenses can be combined in ways analogous to the first two—you can link two lenses to operate in parallel using `alongside` (forming a pair of *glasses*, perhaps) ``` haskell latitude `alongside` longitude :: Lens' (Location, Location) (Arc, Arc) ``` and you can link two lenses such that either the first or the second is used ``` haskell choosing degree minute :: Lens' (Either Arc Arc) Int ``` which you might think of as "teeing" two beams of lensed light together—like a beam splitter run in reverse. It lets us take lenses which focus from *different* locations but into the *same* type and combine then. ## Postscript If this were truly all there were to lenses it might be enough to find a place in your toolkit. They provide a new abstraction—the idea of holding on to a *value* that's *focused* on a constituent of a larger type—and a meaningful algebra for combining (via pairs and eithers, products and coproducts), composing, and modifying these values. They subsume record syntax while minimizing book-keeping on getters and setters. They even include a cute syntax throwing lenses `over` functions. But this is just one module of the more than 40 included in the library---indeed, nothing in this article besides the unnecessary `makeLenses` Template Haskell tricks exists outside of `Control.Lens.Lens`. Furthermore, the same trick that allows us to compose lenses via `(.)` unlocks a very general methodology for thinking about mapping and traversing over data structures that can be taken much further. Later we'll see how to use lenses within monads, to build powerful roundtrip transformations, abstract across cons'able or index'able data structures, or even to subsume zippers and generics. We'll also see how the underlying Rank 2 structure of the lens allows for all of this functionality to be easily composed. So, until next time—cheers! --- Thanks to Serge Le Huitouze for edits.