18 Appendices
This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/optics-by-example.
18.1 Optic Composition Table
This table is adapted from the documentation of the Scala optics library Monocle1. I’ve simply altered it to match the lens library.
The value of each cell denotes the most general type you can achieve by composing the column header with the row header.
The type of an optic is determined by collecting all the constraints of all composed optics in a path. Since constraints collection acts as a set union (which is commutative) the order of composition has no effect on the resulting optic type. Therefore the following table is symmetric across its diagonal.
”–” signifies that the optics are incompatible and do not compose.
| Fold | Getter | Setter | Traversal | Prism | Lens | Iso | |
|---|---|---|---|---|---|---|---|
| Fold | Fold | Fold | – | Fold | Fold | Fold | Fold |
| Getter | Fold | Getter | – | Fold | Fold | Getter | Getter |
| Setter | – | – | Setter | Setter | Setter | Setter | Setter |
| Traversal | Fold | Fold | Setter | Traversal | Traversal | Traversal | Traversal |
| Prism | Fold | Fold | Setter | Traversal | Prism | Traversal | Prism |
| Lens | Fold | Getter | Setter | Traversal | Traversal | Lens | Lens |
| Iso | Fold | Getter | Setter | Traversal | Prism | Lens | Iso |
For example, to determine which type we get by composing traverse with _Just we first check each of their types to discover that traverse is a Traversal and _Just is a Prism. We then look up the column (or row) with the header Traversal, then find the cell with the corresponding header Prism on the other axis. Performing this look up we see that the composition traversed . _Just results in a Traversal.
18.2 Optic Compatibility Chart
The following chart details which optics are valid substitutions for one another.
As an example, let’s say we were curious if all Prisms are a valid Traversal; we first find the row with Prism in the first column; then find the corresponding Traversal column and find a Yes; meaning that a Prism is a valid substitution for a Traversal.
| Fold | Getter | Setter | Traversal | Lens | Review | Prism | Iso | |
|---|---|---|---|---|---|---|---|---|
| Fold | Yes | No | No | No | No | No | No | No |
| Getter | Yes | Yes | No | No | No | No | No | No |
| Setter | No | No | Yes | No | No | No | No | No |
| Traversal | Yes | Yes | Yes | Yes | No | No | No | No |
| Lens | Yes | Yes | Yes | Yes | Yes | No | No | No |
| Review | No | No | No | No | No | Yes | No | No |
| Prism | Yes | No | Yes | Yes | No | Yes | Yes | No |
| Iso | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
18.3 Operator Cheat Sheet
Operators may look like an earthquake hit the mechanical keyboard factory, but there’s actually a bit of a language to the whole thing which starts to make sense after a bit of practice.
Once you get used to the ideas you can usually guess the name of a symbol which does what you need, and it’ll usually exist!
Legend for Getters
| Symbol | Description |
|---|---|
^ |
Denotes a Getter |
@ |
Include the index with the result |
. |
Get a single value |
.. |
Get a List of values |
? |
Maybe get the first value |
! |
Force a result or throw an exception if missing |
Examples
(^@..) :: s -> IndexedFold i s a -> [(i, a)]- A getter (
^) which includes the index (@) in a list of all focuses (..).
"Yarrr" ^@.. folded
[(0,'Y'),(1,'a'),(2,'r'),(3,'r'),(4,'r')]
(^?!) :: s -> Traversal' s a -> a- A getter (
^) which forcibly gets (!) a possibly missing (?) value.
>>> Just "Nemo" ^?! _Just
"Nemo"
>>> Nothing ^?! _Just
*** Exception: (^?!): empty Fold
CallStack (from HasCallStack):
error, called at src/Control/Lens/Fold.hs:1285:28 in lens
E:Control.Lens.Fold
^?!, called at <interactive>:1:1 in interactive:Ghci4
Legend for Setters/Modifiers
| Symbol | Description |
|---|---|
. |
Set the focus |
% |
Modify the focus |
~ |
Denotes a Setter/Modifier |
= |
Denotes a Setter/Modifier over a MonadState context |
< |
Include the altered focus with the result |
<< |
Include the unaltered focus with the result |
%% |
Perform a traversal over the focus |
<> |
mappend over the focus |
? |
Wrap in Just before setting |
+ |
Add to the focus |
- |
Subtract from the focus |
* |
Multiply the focus |
// |
Divide the focus |
|| |
Logically or the focus |
&& |
Logically and the focus |
@ |
Pass the index to the modification function |
Examples
(<>~) :: Monoid a => Traversal' s a -> a -> s -> s- A setter (
~) whichmappends (<>) a new value to the focus.
>>> ["Polly want a", "Salty as a soup"] & traverse <>~ " cracker!"
["Polly want a cracker!","Salty as a soup cracker!"]
(<<%@=) :: MonadState s m => Traversal s s a b -> (i -> a -> b) -> m a-
Modify (
%) the focus from within aMonadState(=), passing the index (@) to the function as well. Also return unaltered (<<) original value.
This one’s a bit tricky:
>>> runState (itraversed <<%@= \k v -> "item: " <> k <> ", quantity: " <> v) (M.sing\
leton "bananas" "32")
("32",fromList [("bananas","item: bananas, quantity: 32")])
(%%~) :: Traversal s t a b -> (a -> f b) -> s -> f t- A setter (
~) which traverses (%%) a function over the focus.
>>> (1, 2) & both %%~ (\n -> if even n then Just n else Nothing)
Nothing
>>> (2, 4) & both %%~ (\n -> if even n then Just n else Nothing)
Just (2,4)
18.4 Optic Ingredients
This content is not available in the sample book. The book can be purchased on Leanpub at http://leanpub.com/optics-by-example.