begriffs

A Clear Intro to Lenses

January 7, 2016

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.

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
  • 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
  • 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