Skip to content

Instantly share code, notes, and snippets.

@ChrisPenner
Last active August 12, 2025 12:57
Show Gist options
  • Save ChrisPenner/1f7b6923448b3396a45d04a2b6b9d066 to your computer and use it in GitHub Desktop.
Save ChrisPenner/1f7b6923448b3396a45d04a2b6b9d066 to your computer and use it in GitHub Desktop.

Revisions

  1. ChrisPenner revised this gist Jan 2, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Optics Cheatsheet.md
    Original file line number Diff line number Diff line change
    @@ -136,7 +136,7 @@ The optic in the first **column** is the optic you **have**, the other column he
    | Fold |||||||||
    | Getter |||||||||
    | Setter |||||||||
    | Traversal || |||||||
    | Traversal || |||||||
    | Lens |||||||||
    | Review |||||||||
    | Prism |||||||||
  2. ChrisPenner renamed this gist Jan 1, 2020. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion Composition.md
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,9 @@
    The following are appendices from [Optics By Example](https://leanpub.com/optics-by-example/), a comprehensive guide to optics from beginner to advanced! If you like the content below, there's **plenty** more where that came from; [pick up the book](https://leanpub.com/optics-by-example/)!

    * [Operators](#operator-cheat-sheet)
    * [Composition Guide](#optic-composition-table)
    * [Substitution Guide](#optic-compatibility-chart)
    * [Optical Constarints and Types](#optic-constraints-and-types)

    ## Operator Cheat Sheet

    @@ -139,7 +142,7 @@ The optic in the first **column** is the optic you **have**, the other column he
    | Prism |||||||||
    | Iso |||||||||

    ## Optic Ingredients
    ## Optic Constraints and Types

    When reading type-errors or strange optics signatures you can usually guess the type of optic an operation needs by matching the constraints against the following chart.

  4. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,8 @@

    The following are appendices from [Optics By Example](https://leanpub.com/optics-by-example/), a comprehensive guide to optics from beginner to advanced! If you like the content below, there's **plenty** more where that came from; [pick up the book](https://leanpub.com/optics-by-example/)!

    * [Operators](#operator-cheat-sheet)

    ## 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.
  5. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -177,3 +177,11 @@ Composing optics adds **constraints** together, then running an optic with an **
    | review (#) | Tagged |
    | traverseOf (%%~) | None |
    | matching | Market |

    ---

    Thanks for reading! If you learned something, consider getting the rest of the book, this cheat sheet only scratches the surface!

    You can get [Optics By Example here](https://leanpub.com/optics-by-example/). Thanks!

    Feel free to share this cheat-sheet with your friends.
  6. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 83 additions and 80 deletions.
    163 changes: 83 additions & 80 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -1,84 +1,6 @@
    ## Optic Composition Table

    This table is adapted from the documentation of the Scala optics library [**Monocle**](https://julien-truffaut.github.io/Monocle/optics.html). 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`.
    # Optics By Example Cheat Sheets

    ## 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 **Prism**s 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.

    The optic in the first **column** is the optic you **have**, the other column headers represent the type you'd like to **use it as**.

    | | Fold | Getter | Setter | Traversal | Lens | Review | Prism | Iso |
    | ---------- | ---- | ------- | ------ | --------- | ---- | ------ | ----- | --- |
    | Fold |||||||||
    | Getter |||||||||
    | Setter |||||||||
    | Traversal |||||||||
    | Lens |||||||||
    | Review |||||||||
    | Prism |||||||||
    | Iso |||||||||

    ## Optic Ingredients

    When reading type-errors or strange optics signatures you can usually guess the type of optic an operation needs by matching the constraints against the following chart.

    Remember that most optics take the form:

    ```haskell
    (a -> f b) -> (s -> f t)
    ```

    However `Iso`s, `Prism`s and `Review`s generalize over the Profunctor type and thus can also have constraints on `p`:

    ```haskell
    p a (f b) -> p s (f t)
    ```

    Keep this in mind when reading the chart.

    | Optic | Constraints |
    |-----------------------|------------------------------- |
    | **Lens** | Functor f |
    | **Fold** | Contravariant f, Applicative f |
    | **Traversal** | Applicative f |
    | **Setter** | Settable f |
    | **Getter** | Contravariant f, Functor f |
    | **Iso** | Functor f, Profunctor p |
    | **Prism** | Applicative f, Choice p |
    | **Review** | Settable f, Profunctor p, Bifunctor p |

    Composing optics adds **constraints** together, then running an optic with an **action** matches a data type which fulfills those constraints. Here's a table of lens actions and the data-type they use to "run" the optics you pass to them:

    | Action | Data type |
    | --------------------------------------------------|-----------|
    | view (^.) | Const |
    | set (.~) | Identity |
    | over (%~) | Identity |
    | fold queries: (toListOf, sumOf, lengthOf, etc.) | Const |
    | review (#) | Tagged |
    | traverseOf (%%~) | None |
    | matching | Market |
    The following are appendices from [Optics By Example](https://leanpub.com/optics-by-example/), a comprehensive guide to optics from beginner to advanced! If you like the content below, there's **plenty** more where that came from; [pick up the book](https://leanpub.com/optics-by-example/)!

    ## Operator Cheat Sheet

    @@ -174,3 +96,84 @@ Nothing
    Just (2,4)
    ```

    ## Optic Composition Table

    This table is adapted from the documentation of the Scala optics library [**Monocle**](https://julien-truffaut.github.io/Monocle/optics.html). 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`.

    ## 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 **Prism**s 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.

    The optic in the first **column** is the optic you **have**, the other column headers represent the type you'd like to **use it as**.

    | | Fold | Getter | Setter | Traversal | Lens | Review | Prism | Iso |
    | ---------- | ---- | ------- | ------ | --------- | ---- | ------ | ----- | --- |
    | Fold |||||||||
    | Getter |||||||||
    | Setter |||||||||
    | Traversal |||||||||
    | Lens |||||||||
    | Review |||||||||
    | Prism |||||||||
    | Iso |||||||||

    ## Optic Ingredients

    When reading type-errors or strange optics signatures you can usually guess the type of optic an operation needs by matching the constraints against the following chart.

    Remember that most optics take the form:

    ```haskell
    (a -> f b) -> (s -> f t)
    ```

    However `Iso`s, `Prism`s and `Review`s generalize over the Profunctor type and thus can also have constraints on `p`:

    ```haskell
    p a (f b) -> p s (f t)
    ```

    Keep this in mind when reading the chart.

    | Optic | Constraints |
    |-----------------------|------------------------------- |
    | **Lens** | Functor f |
    | **Fold** | Contravariant f, Applicative f |
    | **Traversal** | Applicative f |
    | **Setter** | Settable f |
    | **Getter** | Contravariant f, Functor f |
    | **Iso** | Functor f, Profunctor p |
    | **Prism** | Applicative f, Choice p |
    | **Review** | Settable f, Profunctor p, Bifunctor p |

    Composing optics adds **constraints** together, then running an optic with an **action** matches a data type which fulfills those constraints. Here's a table of lens actions and the data-type they use to "run" the optics you pass to them:

    | Action | Data type |
    | --------------------------------------------------|-----------|
    | view (^.) | Const |
    | set (.~) | Identity |
    | over (%~) | Identity |
    | fold queries: (toListOf, sumOf, lengthOf, etc.) | Const |
    | review (#) | Tagged |
    | traverseOf (%%~) | None |
    | matching | Market |
  7. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -26,6 +26,8 @@ The following chart details which optics are valid substitutions for one another

    As an example, let's say we were curious if all **Prism**s 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.

    The optic in the first **column** is the optic you **have**, the other column headers represent the type you'd like to **use it as**.

    | | Fold | Getter | Setter | Traversal | Lens | Review | Prism | Iso |
    | ---------- | ---- | ------- | ------ | --------- | ---- | ------ | ----- | --- |
    | Fold |||||||||
  8. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -28,14 +28,14 @@ As an example, let's say we were curious if all **Prism**s are a valid **Travers

    | | 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 |
    | Fold | | | | | | | | |
    | Getter | | | | | | | | |
    | Setter | | | | | | | | |
    | Traversal | | | | | | | | |
    | Lens | | | | || | | |
    | Review | | | | | | | | |
    | Prism | | | | | | | | |
    | Iso | | | | || | | |

    ## Optic Ingredients

    @@ -136,7 +136,7 @@ E:Control.Lens.Fold
    | `-` | **Subtract** from the focus |
    | `*` | **Multiply** the focus |
    | `//` | **Divide** the focus |
    | `||` | Logically **`or`** the focus |
    | `\|\|` | Logically **`or`** the focus |
    | `&&` | Logically **`and`** the focus |
    | `@` | Pass the **index** to the modification function |

  9. ChrisPenner revised this gist Jan 1, 2020. 1 changed file with 154 additions and 3 deletions.
    157 changes: 154 additions & 3 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,6 @@
    ## Optic Composition Table

    This table is adapted from the documentation of the Scala optics library **Monocle**[^monocle]. I've simply altered it to match the `lens` library.

    [^monocle]: https://julien-truffaut.github.io/Monocle/optics.html
    This table is adapted from the documentation of the Scala optics library [**Monocle**](https://julien-truffaut.github.io/Monocle/optics.html). 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.

    @@ -21,3 +19,156 @@ The type of an optic is determined by collecting all the constraints of all comp
    | **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`.

    ## 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 **Prism**s 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 |

    ## Optic Ingredients

    When reading type-errors or strange optics signatures you can usually guess the type of optic an operation needs by matching the constraints against the following chart.

    Remember that most optics take the form:

    ```haskell
    (a -> f b) -> (s -> f t)
    ```

    However `Iso`s, `Prism`s and `Review`s generalize over the Profunctor type and thus can also have constraints on `p`:

    ```haskell
    p a (f b) -> p s (f t)
    ```

    Keep this in mind when reading the chart.

    | Optic | Constraints |
    |-----------------------|------------------------------- |
    | **Lens** | Functor f |
    | **Fold** | Contravariant f, Applicative f |
    | **Traversal** | Applicative f |
    | **Setter** | Settable f |
    | **Getter** | Contravariant f, Functor f |
    | **Iso** | Functor f, Profunctor p |
    | **Prism** | Applicative f, Choice p |
    | **Review** | Settable f, Profunctor p, Bifunctor p |

    Composing optics adds **constraints** together, then running an optic with an **action** matches a data type which fulfills those constraints. Here's a table of lens actions and the data-type they use to "run" the optics you pass to them:

    | Action | Data type |
    | --------------------------------------------------|-----------|
    | view (^.) | Const |
    | set (.~) | Identity |
    | over (%~) | Identity |
    | fold queries: (toListOf, sumOf, lengthOf, etc.) | Const |
    | review (#) | Tagged |
    | traverseOf (%%~) | None |
    | matching | Market |

    ## 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** (`..`).

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

    ```haskell
    >>> 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 |
    | `%` | **Mod**ify 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** (`~`) which `mappend`s (`<>`) a new value to the focus.

    ```haskell
    >>> ["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 a `MonadState` (`=`), passing the **index** (`@`) to the function as well. Also return **unaltered** (`<<`) original value.


    This one's a bit tricky:

    ```haskell
    >>> runState (itraversed <<%@= \k v -> "item: " <> k <> ", quantity: " <> v) (M.singleton "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.

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

  10. ChrisPenner created this gist Jan 1, 2020.
    23 changes: 23 additions & 0 deletions Composition.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    ## Optic Composition Table

    This table is adapted from the documentation of the Scala optics library **Monocle**[^monocle]. I've simply altered it to match the `lens` library.

    [^monocle]: https://julien-truffaut.github.io/Monocle/optics.html

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