# Cargo Culting Lenses for Fun & Profit

sean.chalmers@data61.csiro.au

### The Point

• To really use lenses, you will need to learn the theory.
• However, you don't need to learn the theory to start using lenses.
• I hope to give you that start on the rote application of lenses.
• I am no expert

### Sacks of Lenses

• I'll be using Haskell `lens` package.
• OCaml has lenses, `ocaml-optics`, `ocaml-lens`.
• Scala too, `monocle`.

### Lens ~ Getter & Setter

``````-- Getter
(^.) :: s -> Getting a s a -> a
-- Setter
(.~) :: ASetter s t a b -> b -> s -> t``````

Talk over, right?

### Simplest Simples

``````data Foo = Foo
{ _petCamelName     :: Text
, _petCamelTopSpeed :: Int
}
-- Template Haskell to write the lenses for us
makeLenses ''Foo``````

### Getter

``_petCamelName foo = "Fred" = foo ^. petCamelName``

Yay?

### [Inception Movie Pun]

Reaching deeper into a data structure:

``````data Bar = Bar { _camelPerson :: Foo }

"Sally" = bar ^. camelPerson . petCamelName
--
"Sally" = _petCamelName . _camelPerson \$ bar``````

### Lenses are functions

Lenses compose to make new lenses:

``````camelPerson :: Lens' Bar Foo
petCamelName :: Lens' Foo Text``````

Compose these with (`.`)

``camelPerson . petCamelName :: Lens' Bar Text``

### Setter

Simple replacement using `(.~)`

``````(petCamelName .~) :: Text -> Foo -> Foo
--
a & petCamelName .~ "Sally" = a { _petCamelName = "Sally" }``````

Using reverse apply `(&)`:

``foo & petCamelName .~ "Sally"``

What about updating a thing in a thing in a thing?

``````nestedUpdate a =
let
nn = _petCamelName . _camelPerson \$ a
in
a { _camelPerson =
_camelPerson a {
_petCamelName = nn <> " the Wise"
}
}``````

### Another time, another place

``````Helper.returnNullOrCallFunction(
Helper.returnNullOrCallFunction(
myObject.getSomeOtherObject(),
SomeOtherObject::getAnotherObject
),
AnotherObject::increaseTheThing
);``````

Taken from: https://stackoverflow.com/a/26414202

### But using lenses

``````nestedUpdate =
-- Using `set with function`
camelPerson . petCamelName %~ (<> " the Wise")
-- Using a mappend setter
camelPerson . petCamelname <>~ " the Wise"``````

``````foo & petCamelName .~ "Sally"
& petCamelTopSpeed .~ 48
--
camelPerson %~ \f -> f
& petCamelName .~ "Sally"
& petCamelTopSpeed .~ 48``````

### Update with a function

Apply a function to your lens target

``petCamelTopSpeed %~ (+10)``

There are heaps of prebaked operators:

``````petCamelTopSpeed +~ 10           -- Add 10 cabbages
petCamelTopSpeed //~ 2           -- Divide by 2 cabbages
petCamelName     <>~ " the Wise" -- Reward victory with a title``````

### Getting interesting

Geddit? eh? eh? ...fine.

``data A = A { _bars :: [Bar] }``
• Get the names of all the camels
• Update speed values
• What function could possibly achieve such a thing?

### `traverse` <3

• ``tBars = bars . traverse``
• ``````-- (^..) is an alias of 'toListOf'
a ^.. tBars . camelPerson . petCamelName``````
• ``````-- Works with setters too, of course
tBars . camelPerson . petCamelTopSpeed +~ 10``````
• ``````-- Can we do this? What is the answer?
a ^. tBars . camelPerson . petCamelName``````

### Traversal

``````tBars :: Traversal' A Bar
tBars = bars . traverse``````

Which we can extend and reuse!

``````topSpeeds :: Traversal' A Int
topSpeeds = tBars . camelPerson . petCamelTopSpeed``````

Exercise: Write the same function without lens

``topSpeeds :: Applicative f => (Int -> f Int) -> A -> f A``

### Fold

Composing a Getter with a Traversal will yield a Fold.

``````-- We saw a Fold earlier : (^..)
a ^.. tBars . camelPerson . petCamelName``````
``````-- Only want the first camel person?
a ^? bars . _head :: Maybe Bar``````

### Lens Family Tree

``````  Fold   Setter
/  \_____/
\ /      \
Getter  Traversal
/  ____/
\ /    \
Lens   Prism
\   /
Iso``````

### Lets get wild

Update a value in a StateT?

``````-- Assuming : StateT Foo m
petCamelName     .= "Bub"         -- Replace
petCamelTopSpeed %= (-10)         -- Map
petCamelName     <>= " the Swift" -- Mappend``````
• What if it's in a thing in the thing in StateT?
• ``````-- Assuming : StateT Bar m
camelPerson . petCamelTopSpeed %= (-10)``````

Update several different fields on a record.

``````\n ds ->
ds & dataSet_max %~ max n
& dataSet_min %~ min n
& dataSet_lines . traverse %~
( ( line_end . _Point . _1 +~ stepToTheRight )
. ( line_start . _Point . _1 +~ stepToTheRight )
)
& dataSet_lines %~ (\xs -> addNewDataPoint n xs (uncons xs))``````

### The Heck was `_Point` / `_1`

That was a `Prism`.

• Prisms are Traversals that may become Getters.
• One way to think of Prisms is as a Lens that is partial in one direction, with laws.
• ``````Right 'c' ^? _Left == Nothing
Left 'c' ^? _Left == Just 'c'``````
• ``5 ^. re _Left == Left 5``
• ``( ( Left 5 ) & _Left .~ 6 ) == Left 6``

### Handling Failure

Consider the following JSON:

``````{ "alpha":
{ "beta":
[.., { "gamma": [.., {"delta": <some bool> }, ...] }, ..]
}
}``````
• Assuming `gamma` is at index 3, and `delta` is at index 2. > Your mission is to flip the value at `delta`.

### First Hack

``````a = getAtKey blob "alpha"
if ( null != a ) {
b = getAtKey a "beta" {
if ( null != b && isArray b ) {
c = getAtIndex b 3
if ( null != c ) {
fooList = getAtKey "gamma"
if ( null != fooList && isArray fooList ) {
fooObj = getAtIndex c 2
if ( null != fooObj ) {
fooVal = getBoolValAtKey "delta" fooObj
if ( null != fooVal ) {
newFoo = setValueAtKey "delta" (not fooVal)
return ( setValueAtKey "beta" a
( setValueAtIndex 3 b
( setValueAtKey  "gamma" c
( setValueAtIndex 2 fooList newFoo )
)
)
)
}
}
}
}
}
}
}``````

This pseudo-code is an exaggeration, but not by much and you know it.

### Yay

Using `lens-aeson` and `lens`.

``````( key "alpha"
. key "beta" . _Array . ix 3
. key "gamma" . _Array . ix 2
. key "delta" . _Bool %~ not
) :: AsValue t => t -> t``````

ahem

### Prisms

The `_Array` and `_Bool` things are both a `Prism`.

``````"[1,2,3]" ^? _Array
--
[1,2,3] ^. re _Array``````

### Prisms are Traversals

``````Just 3 ^? _Just = Just 3
Nothing ^? _Just = Nothing``````
``('a', Just 3) & _2 . _Just %~ (+1) = ('a', Just 4)``

### Lens Operator Grammar

Lens operators are formed from a symbol DSL.

• `^` - refers to a fold
• `~` - modification or setting
• `?` - results optional
• `<` - return the new value
• `<<` - return the old value
• `%` - use a given function
• `%%` - use a given traversal
• `=` - apply lens to `MonadState`
• `.` - which side should have the lenses

### Modifying a Map

``````let
m = Map.fromList [(1, "susan"), (2, "jo")]
in
m & at 1 .~ "bob"
& at 3 ?~ "pixie"
& at 2 %~ _f :: ??``````

### Oh my

Modifying a map in your StateT, setting a new value if one exists, but creating a new key-value pair if it doesn't exist, and returning the previous value if there was one?

``````data A = A
{ _theFoo :: Foo
, _theMap :: Map String Int
}``````
• ``a <- theMap . at "foo" <<?= 37``

### The 'Of' family

Use a lens to target a part of a structure.

• traverseOf, itraverseOf
• maximumOf, minimumOf
• foldMapOf, mapMOf