Lenses are immensely useful to the Haskell programmer, but suffer from a discovery problem — without enough exposure or experience, it’s hard to know which operator to use in a given situation. This post provides explanations for common lens operators as well as example-driven references for developers just getting started with lenses.
There is prior art for lens-related reading — we’ve pulled together a short list at the bottom of this post, including one from another Urbinite. The Lens Library itself also provides documentation and usage examples, once you know what you’re looking for. The Lens website is a great place to start.
The motivation for this post is an attempt at smoothing out and condensing the
Lens learning curve. Using
Lens requires familiarity with several new concepts encoded in a handful of new operators. The learning process was frustrating — I knew the problems I was facing had a simple answer. I spent too long staring blankly at
folded operators, frustrated that all I wanted was to list the values in my HashMap.
Lens makes this very easy, but only if you’ve done it before.
So what the heck is a lens?
Control.Lens is a Haskell package written by Edward Kmett. It provides ‘Optics’ for working with data structures; these optics let you access and update values (
set) as well as modify the structure itself (for example, converting a HashMap into a HashSet).
Control.Lens package, a
Lens itself is the place to start. A lens can be defined as both a
Setter for the same data structure. The same lens can then be used in different contexts to
set a value. This is useful on its own, but the real power comes from composition — multiple lenses can be composed together and used as one, allowing you to
set arbitrarily nested structures with ease. If this is not sticking, fear not — we’ll get into the nitty-gritty details in the examples below.
Lens package is very large — in this post, we’re only going to cover examples for the following:
preview (^?), and
These together cover many use-cases, and will speed up your Haskell productivity. We’ll visit some more advanced operators in a future post. If there are any you’d like covered in a similar way, please leave a comment!
First things first — you NEED instant operator documentation lookup. If you’re on a Mac, this can be done via Alfred and Dash. Setup is on your own, but to motivate you, searching for a
Lens operator like
^. can be done via
[cmd]+[space] hs ^..
This workflow feature is crucial for quickly looking up operators you don’t know, which should help you feel less like
!?!?!?, and more like “Oh,
^?! is just an unsafe view on
Tooling like this is critical for searching for obscure operators and super generic function names.
set are not quite specific enough for today’s Google.
This was originally written as a Literate Haskell file (.lhs), which can be compiled and run like any other Haskell file. The source document can be found here. See the Readme in that repo for instructions for running it yourself.
Our Data Structures
Let’s create a few data types to work with.
Now let’s write some lenses for our data types.
Remember that lenses can be defined as a
Getter and a
Setter between two structures.
Advanced Usage: Note that it is possible to generate lenses with Template Haskell! We’re not going to get into that magic in this post — I recommend making sure you know how to write these yourself first.
Great. Let’s put these lenses to work.
`view` (^.), `
preview` (^?), and
view is used for applying the
Getter in your lenses to the
a of your choice.
Mnemonic: The use of the carrot
^is quite literal — it’s an upside down ‘v’, as in
view. Beyond that,
preview|^?is used to differentiate
If that’s not mnemonic enough, wait until we get to
over’s use of
% as a pun on
mod(ulo/ify). You’ll be all, “smh Ed Kmett, you are one cheeky operator.”
The best part about lenses, of course, is that they compose!
But what’s this?
^?!? Did you just drop a new operator out of nowhere? Welcome to Haskell, where the operators are fun toy things anyone can drop in anywhere! Whenever you see a new operator, I encourage you to laugh maniacally. It might help.
preview (and its infix version (
^?)) are similar to
(^.). The difference is that
preview lets us walk over Folds and Traversal, in this case, the
_Just is a Prism providing a Traversal for targeting the
Just a of a
Maybe a. I’m sure you caught all of that. More examples please!
We’re not going to dig heavily into Folds, Traversals, or Prisms themselves in this post. Rather, we’ll stick close to usage examples, and learn enough practical
Lensknowledge that exploring those on your own later is a little more sane.
I don’t know if there’s a named version for
^?!. Maybe it’s
unsafePreview? I’m sure there are plenty of snarky
view/preview related extended metaphors to explore.
set lets us update values on our data structures, using the same lenses we used to view those values.
.~ (tilda) should be read similar to the
= operator in an imperative language — it sets the value on the right to be the target of the lens, and returns the updated object.
.~as a kind of side-ways ‘s’. Seriously.
Advanced Lens Note:
=is often swappable for
~lens operators when working in a
Let’s check out some
What’s this? An ampersand?
&?!? Did you laugh like a super villain? I hope you did.
If you ask a seasoned Haskell Lenser what this
& is all about, they’ll say, oh, it’s just the reverse application operator, duh. It’s an inverted
& makes updating an object with
set|.~ easier. The advice I got was to read it as “with”. That last line should read: “print bob with score set to 42”.
You know what else is cool? You can chain
&s to update multiple targets on the same object. Mind blown, amirite?
So now you can view and set with lenses, and compose lenses arbitrarily. Remember that setting is not just an “update,” but at times a “delete,” depending on the lens — we’ll see an example of that when we get to
But first! You probably want something a little more flexible than set — say you wanted to increment Bob’s score, but don’t want to use lenses multiple times to view then add to it. (Let’s also say you didn’t know Ed Kmett already wrote
+~ for this exact use-case).
over (%~) is like
set (.~), but takes a function from
a -> b rather than just
b. That way you can pass a function rather than a value to replace whatever the target of the lens is.
Mnemonic: I alluded to the theory behind the
%earlier — this is your
modoperator, used as a kind of nerdy pun on ‘modulo’/’modify’. Yep.
over is pretty handy! I hit a use-case for wrapping errors the other day that I rather liked:
The use of
over above uses
_Left Prism to target the Either’s
Left a cases, and apply the function to that target if a target is found. If it’s
Right b, no target is found, and the object is returned unmodified.
`at` and `ix`
ix are for things that are indexed. Maps, HashMaps, Lists — collections with keys or indexes.
ix are some of my favorite Optics — once they are in your repertoire, you’ll be hella annoyed when you have to work with indexed structures in other languages.
ix are roughly the same, with a key difference —
ix is a Traversal, while
at is a Lens. You don’t know what a Traversal is? Geez, you weren’t born spouting Category Theory? You probably didn’t even know Redux is just a big Monadic Klieisli composition (
>=>). Even Fitzgerald knows that, and he’s just a goldfish.
Traversals are different from Lenses (and Folds) in this context because a Traversal cannot change the structure of the thing being traversed — it can adjust the values in place, but it cannot add or remove elements. Thus, Ix makes for more convenient in place adjustments, while At is useful for adding and removing elements.
You may be wondering why we don’t just us
at for everything — indeed, you can if you’d like. However,
at is a lens to
Maybe a, while
ix is a traversal to
a. It’s the
?~/^? to your
.~/^. Ix-y goodness. As a result,
at requires you to use a
_Just or a
preview/?~, which is more verbose than is necessary.
A more practical reason is that, at times,
at cannot be legally implemented. You may write a custom data structure for which the
Lens laws are not satisfied with
at (this happened to us at Urbint!). Because
ix and Traversals cannot modify the structure itself, there are fewer requirements to a valid implementation.
atis named such because it refers to an element “at” a key, while
ixis said to represent the “i-th” element in a structure. Both, however, can take keys of any type, as long as you implement the required type classes for that key.
Enough jabbering. Let’s see what
ix can do!
Per the monoid instance mentioned above — it is worth mentioning and showing a case for another optic called
non. First an example, then an explanation.
non is built with a default value, and it targets a
Maybe a. When the lens is used, if the value found is a
Nothing, the default value is used, and the lens continues. If the value is
Just a, the
a is passed along as it is.
In the above example, the lens to Bob’s at “gold” results in a
defaultGoldItem is used in its place, and the
value lens operates over that default item.
non is very useful when paired with typeclasses, like
`toListOf` (^..) and `folded`
Structures like HashMaps are useful for looking things up by key — but how do you get a list of values in your HashMap? I was hung up on this for a while, even after crawling
Control.Lens.Fold. The answer:
(^..)) combined with
There’s more to
folded and working with
toListOf, but this is enough to get you started.
`has` and `hasn’t`
has is a useful operator with a big gotcha. From the docs,
has checks to see if a passed Fold or Traversal matches one or more entries. The thing to note is that it will ALWAYS return true for a lens.
What? True? Remember that
at is a lens. If you want this to be useful, you’ll have to use
ix, our resident indexed Traversal.
And just for kicks, an example of
There are plenty of more useful operators and optics in
Lens. If you’re hungry for more, here’s more lens material from around the web.
- Kmett’s cheat sheet in the lens wiki
This cheat sheet has decent coverage, and answers a handful questions in one place.
- Quchen’s lens-infix-operators article
A concise break down of the reusable parts of lens operators — super useful and a quick read and reference. (Thanks to PinkyThePig on Reddit for this)
- Artyom’s Lenses over tea
Admittedly more lens implementation details than practical use — but perhaps that’s what you’re looking for!
- Gabriel Gonzales’s lens tutorial
A great tutorial for getting started with lenses.
- The Lens library’s Big PDF of the lens family
- The Lens library’s Readme walkthrough
- Wikibook lens chapter
I hope this helped you on your Haskell journey! There is plenty more to cover in a follow up guide — if this was useful, let us know which concepts you’d like covered next.