Haskell Lens Operator Onboarding

Russell Matney
9 min readMay 22, 2018

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 Lens’s at/ix and 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 (view and set) as well as modify the structure itself (for example, converting a HashMap into a HashSet).

Within the Control.Lens package, a Lens itself is the place to start. A lens can be defined as both a Getter and Setter for the same data structure. The same lens can then be used in different contexts to view or 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 view or 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.

The Lens package is very large — in this post, we’re only going to cover examples for the following:

  • view (^.), preview (^?), and (^?!)
  • set (.~)
  • over (%~)
  • at and ix
  • toListOf (^..) and folded
  • has and hasn’t

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!

Search

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 ^..

Searchable Lens documentation on OSX via Alfred and Dash integration.

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 Maybe a”.

Tooling like this is critical for searching for obscure operators and super generic function names. view, and set are not quite specific enough for today’s Google.

If an OS built-in tool is not available, the “Index” pages on Hackage can also make quick work of finding an operator exposed by a library. Here’s the index page for the Lens library.

Literate Haskell

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.

Our Lenses

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, view|^. vs preview|^? is used to differentiate a from Maybe a.

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? _Just? preview and ^?!? 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 view and (^.). The difference is that preview lets us walk over Folds and Traversal, in this case, the Maybe Pet. _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 Lens knowledge 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. viewbang? Maybe?

`set` (.~)

set lets us update values on our data structures, using the same lenses we used to view those values.

The .~ (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.

Mnemonic: .~ as a kind of side-ways ‘s’. Seriously.

Advanced Lens Note: = is often swappable for ~ lens operators when working in a StateT context.

Let’s check out some set examples.

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 $.

The & 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 at.

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` (%~)

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 mod operator, 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 Lens’s _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`

at and ix are for things that are indexed. Maps, HashMaps, Lists — collections with keys or indexes. at and 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.

at and 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.

Mnemonic: at is named such because it refers to an element “at” a key, while ix is 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 at and 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 Nothing, so defaultGoldItem is used in its place, and the value lens operates over that default item.

non is very useful when paired with typeclasses, like Monoid and mempty or Default and def.

`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: toListOf (aka (^..)) combined with folded.

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 hasn’t.

Next Steps

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.

Conclusion

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.

If you get excited about working with Haskell, machine learning, and urban data, please talk to us! Urbint is helping urban operators plan for what’s next.

--

--