# 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?

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"``````

### Multiple Updates

``````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)``````

### Giant Updates

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

### How I learned

• Haddock diving
• Playing in the REPL
• "Lets Lens" introductory course

### Trying it yourself

REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL REPL

### Thanks for suffering through

Questions ?

Lets Lens! - https://github.com/data61/lets-lens