Skip to content
Lenses (intro)
step 1/4

Reading — step 1 of 4

Learn

~2 min readType-Level Programming

Updating nested immutable data is annoying:

data Address = Address { street :: String, city :: String }
data User = User { name :: String, addr :: Address }

-- Change a user's city:
updateCity :: User -> String -> User
updateCity u newCity = u { addr = (addr u) { city = newCity } }

It gets worse with deeper nesting. Lenses are first-class getters/setters that compose.

import Control.Lens

data Address = Address { _street :: String, _city :: String }
data User = User { _name :: String, _addr :: Address }

makeLenses ''Address     -- TemplateHaskell — generates `street`, `city` lenses
makeLenses ''User         -- generates `name`, `addr` lenses

-- Now:
user ^. addr . city                       -- get city  (^. is "view")
user & addr . city .~ "Paris"             -- set city  (.~ is "set")
user & addr . city %~ map toUpper         -- map over city  (%~ is "over")

Composition with . — chain lenses to drill into nested fields. The same . is function composition; lenses compose like functions.

Operators:

  • ^. — view (get)
  • .~ — set
  • %~ — over (apply function)
  • & — flipped function application: x & f & g = g (f x)

Without TemplateHaskell — write lenses manually:

name :: Lens' User String
name = lens _name (\u n -> u { _name = n })

The lens smart constructor takes a getter and a setter.

Traversal — generalized over multiple values:

user & addresses . traverse . city .~ "Paris"   -- set city in EVERY address

Prism — for sum types. Like a partial lens:

user ^? addr . _Just . city                       -- Maybe String

Why use lenses:

  • Updating deeply nested data without verbosity
  • Writing generic functions that work on "the X-part of any structure"
  • Pattern-matching with traversal (modify all matching elements)

Caveats:

  • lens package is HUGE — many projects use microlens or optics for smaller scope
  • TemplateHaskell adds compile time
  • The operator soup is intimidating at first

Lenses are mostly worth it for codebases with lots of nested record updates.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…