April 16, 2020

A quick over view of lens

You’ve completed the Data61 FP Course. You’ve gone on and finished every single exercise in the QFPL Applied FP Course. You know what you’re doing, so you join a Haskell project, and are presented with:

update things thing =
  let thatThing = Map.lookup (thing ^. properties . identifier) things
      thingName = thatThing <&> view (metadata . name)
      thingResponsibilities = thatThing <&> view responsibilities
   in thing & name .~ thingName
            & responsibilities <>~ thingResponsibilities
            & age %~ (* 2)

This looks nothing like the language you’ve been using! How can it be this different? How are you supposed to contribute to this project?!

Well, it would help if you could read lens syntax. This post is what I wish I had the first time I encountered code like this.

& and <&>

x & f

is equivalent to

f $ x

which is just

f x

The two syntaxes can be interlaved. Pretend you’re in an object-oriented language and read x & p & q as x.p.q. So:

f $ g $ x & p & q $ h

is equivalent to

f (g ((q (p x)) h))

Similarly,

x <&> f

is equivalent to

f <$> x

Automatically generated lenses

This syntax:

data MyRecord {
  _property :: String,
  _otherProperty :: Int
}
makeLenses ''MyRecord

generates these additional definitions:

property :: Lens' MyRecord String
otherProperty :: Lens' MyRecord Int

Don’t worry about what Lens' means, just know that you won’t be able to use grep to locate the definitions of thatProperty and otherProperty. Try looking for the property prefixed with _.

Reading properties out of things

thisThing ^. property . subProperty . subSubProperty

is equivalent to

view (property . subProperty . subSubProperty) thisThing

and

_subSubProperty . _subProperty . _property $ thisThing

Changing properties of things

thisThing & property . subProperty . subSubProperty .~ newValue
          & property . otherProperty %~ updateValue
          & otherStuff <>~ stuffs

is equivalent to

over otherStuff (<> stuffs)
  $ over (property . otherProperty) updateValue
  $ set (property . subProperty . subSubProperty) newValue
  $ thisThing

and

thisThing {
  _property = _property thisThing {
    _subProperty = _subProperty (_property thisThing) {
      _subSubProperty = newValue
    },
    _otherProperty = updateValue (_otherProperty (_property thisThing)),
    _otherStuff = _otherStuff (_property thisThing) <> stuffs
  }
}

This is… not very readable. No wonder lenses were invented!

Powered by Hugo & Kiss.