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:
lenspackage is HUGE — many projects usemicrolensoropticsfor 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…