- A+

I'm trying to achieve a deeper understanding of `lens`

library, so I play around with the types it offers. I have already had some experience with lenses, and know how powerful and convenient they are. So I moved on to Prisms, and I'm a bit lost. It seems that prisms allow two things:

- Determining if an entity belongs to a particular branch of a sum type, and if it does, capturing the underlying data in a tuple or a singleton.
- Destructuring and reconstructing an entity, possibly modifying it in process.

The first point seems useful, but usually one doesn't need all the data from an entity, and `^?`

with plain lenses allows getting `Nothing`

if the field in question doesn't belong to the branch the entity represents, just like it does with prisms.

The second point... I don't know, might have uses?

So the question is: what can I do with a Prism that I can't with other optics?

**Edit**: thank you everyone for excellent answers and links for further reading! I wish I could accept them all.

Here's the headline:

**Lenses characterise the has-a relationship; Prisms characterise the is-a relationship.**

A `Lens s a`

says "`s`

*has an* `a`

"; it has methods to get exactly one `a`

from an `s`

and to overwrite exactly one `a`

in an `s`

. A `Prism s a`

says "`a`

*is an* `s`

"; it has methods to upcast an `a`

to an `s`

and to (attempt to) downcast an `s`

to an `a`

.

Putting that intuition into code gives you the familiar "get-set" (or "costate comonad coalgebra") variety of lenses,

`data Lens s a = Lens { get :: s -> a, set :: a -> s -> s } `

and an "upcast-downcast" representation of prisms,

`data Prism s a = Prism { up :: a -> s, down :: s -> Maybe a } `

`up`

injects an `a`

into `s`

(without adding any information), and `down`

tests whether the `s`

is an `a`

.

In `lens`

, `up`

is spelled `review`

and `down`

is `preview`

. There’s no `Prism`

constructor; you use the `prism'`

smart constructor.

What can you do with a `Prism`

? Inject and project sum types!

`_Left :: Prism (Either a b) a _Left = Prism { up = Left, down = either Just (const Nothing) } _Right :: Prism (Either a b) b _Right = Prism { up = Right, down = either (const Nothing) Just } `

Lenses don't support this - you can't write a `Lens (Either a b) a`

because you can't implement `get :: Either a b -> a`

. As a practical matter, you *can* write a `Traversal (Either a b) a`

, but that doesn't allow you to create an `Either a b`

from an `a`

- it'll only let you overwrite an `a`

which is already there.

Aside:I think this subtle point about`Traversal`

s is the source of your confusion about partial record fields.

`^?`

with plain lenses allows getting`Nothing`

if the field in question doesn't belong to the branch the entity representsUsing

`^?`

with a real`Lens`

will never return`Nothing`

, because a`Lens s a`

identifies exactly one`a`

inside an`s`

. When confronted with a partial record field,`data Wibble = Wobble { _wobble :: Int } | Wubble { _wubble :: Bool }`

`makeLenses`

will generate`Traversal`

s, not`Lens`

es.`wobble :: Traversal' Wibble Int wubble :: Traversal' Wibble Bool`

For an example of this upcast-downcast notion of `Prism`

s in the real world, look to `Control.Exception.Lens`

, which provides a collection of `Prism`

s into Haskell's extensible `Exception`

hierarchy. This lets you perform runtime type tests on `SomeException`

s and inject specific exceptions into `SomeException`

.

`_ArithException :: Prism' SomeException ArithException _AsyncException :: Prism' SomeException AsyncException -- etc. `

(These are slightly simplified versions of the actual types. In reality these prisms are overloaded class methods.)

To summarise, `Lens`

es and `Prism`

s together encode the two core design tools of object-oriented programming, composition and subtyping. `Lens`

es are a first-class version of Java's `.`

and `=`

operators, and `Prism`

s are a first-class version of Java's `instanceof`

and implicit upcasting.

One fruitful way of thinking about `Lens`

es is that they give you a way of splitting up a composite `s`

into a focused value `a`

and some context `c`

. Pseudocode:

`type Lens s a = exists c. s <-> (a, c) `

In this framework, a `Prism`

gives you a way to look at an `s`

as being either an `a`

or some context `c`

.

`type Prism s a = exists c. s <-> Either a c `

(I'll leave it to you to convince yourself that these are isomorphic to the simple representations I demonstrated above. Try implementing `get`

/`set`

/`up`

/`down`

for these types!)

In this sense a `Prism`

is a *co- Lens*.

`Either`

is the categorical dual of `(,)`

; `Prism`

is the categorical dual of `Lens`

.You can also observe this duality in the "profunctor optics" framework - `Strong`

and `Choice`

are dual.

`type Lens s t a b = forall p. Strong p => p a b -> p s t type Prism s t a b = forall p. Choice p => p a b -> p s t `

This is more or less the representation which `lens`

uses, because these `Lens`

es and `Prism`

s are very composable. You can compose `Prism`

s to get bigger `Prism`

s ("`a`

*is an* `s`

, which *is a* `p`

") using `(.)`

; composing a `Prism`

with a `Lens`

gives you a `Traversal`

.