Accessing nested data in Haskell without lenses is “horrifically annoying,” confides Roger Curley. In this talk Roger takes a magnifying glass to lenses, showing how they emerged from the combination of structure modification functions with functors. After motivating lens construction and stepping through how they are evaluated at runtime he finishes with an example of consuming a JSON API.

Download Video: (HD / SD).

Summary

Haskell’s record accessors are tolerable for simple data

But updating records with record accessors is a pain once you nest For example, imagine a detailed simulation game with dwarves Say each dwarf has eyes, eyes have a color, and colors have RGB components To update a dwarf’s eye color you have to get each element in the chain of nesting, make the change and package it back up The amount of code you have to write is quadratic in the depth of the nesting!

Let’s invent our way out of this trap

First attempt: make a bunch of updater functions For instance modifyRed :: (Int -> Int) -> (Color -> Color) and modifyEyeColor :: (Color -> Color) -> (Eye -> Eye) Such functions are composable, modifyEye . modifyColor

This is almost sane! You could stop here and your life would not be the worst.

Let’s keep going. You can’t use these updater functions to view things. We could take a peek at values in the updater and save the value if we use unsafePerformIO… But if we want to have principles it’s going to be harder Functors to the rescue (quick review) The Const and Identity functors in particular will be useful

We’ll tweak our modification functions to use functors Rather than (Int -> Int) -> (Color -> Color) we’ll use (Int -> f Int) -> (Color -> f Color) The lens library provides an over function which will recover our original function from its functor version It uses the Identity functor to do this Visualizing exactly how it gets evaluated in Haskell

How to view values with lenses rather than update them? Use the Const functor Make a view function that is just like over but uses a different functor

Now we’re almost at the level of technical prowess of…any object oriented language ever We can use a template haskell function makeLenses which will inspect any data type and create lenses for any records whose names begin with a period

Examples of applying view , set , and over for the Dwarf eyes

, , and for the Dwarf eyes OK, the original lens definition (a -> f a) -> (s -> f s) was a simplification The real lens is generalized to be able to change the type of the data you’re lensing

was a simplification Lenses have to obey laws Example of the at lens for maps Arguably there are many lenses that you may think exist or want to write, but which are impossible to write with the tools we have seen thus far For example, just to access the Just component of a Maybe is impossible to write Same deal with head

To broaden the lens possibilities we can turn to Applicatives We can make Const an Applicative instance for monoids We can use pure to handle problem cases like Nothing for the _just lens or [] for _head .

JSON example Reading a property deep in a response from the Wikipedia API



Related Posts