Nathan Wilson - 27 days ago 5

Scala Question

In functional optics, a well-behaved prism (called a partial lens in scala, I believe) is supposed to have a *set* function of type

`'subpart -> 'parent -> 'parent`

`'parent`

`'parent`

`'subpart`

`'parent`

`'parent`

I'm wondering why the prism doesn't return a

`'parent option`

`Maybe`

I know there's been a lot of research and thought put into the realm of functional optics, so I'm sure there must be a definitive answer that I just can't seem to find.

(I'm from an F# background, so I apologize if the syntax I've used is a bit opaque for Haskell or Scala programmers).

Answer Source

To answer the “why” – lenses etc. are pretty rigidly derived from category theory, so this is actually quite clear-cut – the behaviour you describe just drops out of the maths, it's not something anybody defined for any purpose but follows from far more general ideas.

Ok, that's not really satisfying.

Not sure if other languages' type systems are powerful enough to express this, but in principle and in Haskell, a prism is a special case of a *traversal*.
A traversal is a way to “visit” all occurences of “elements” within some “container”. The classical example is

```
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
```

This is typically used like

```
Prelude> mapM print [1..4]
1
2
3
4
[(),(),(),()]
```

The focus here is on: sequencing the actions/side-effects, and gathering back the result in a container with the same structure as the one we started with.

What's special about a prism is simply that the containers are restricted to contain either one or zero elements^{†} (whereas a general traversal can go over any number of elements). But the `set`

operator doesn't know about that because it's strictly more general. The nice thing is that you can therefore use this on a lens, or a prism, or on `mapM`

, and always get a sensible behaviour. But it's *not* the behaviour of “insert exactly once into the structure or else tell me if it failed”.

Not that this isn't a sensible operation, just it's not what lens libraries call “setting”. You can do it by explicitly matching and re-building:

```
set₁ :: Prism s a -> a -> s -> Maybe s
set₁ p x = case matching p x of
Left _ -> Nothing
Right a -> Just $ a ^. re p
```

^{†}_{More precisely: a prism seperates the cases: a container may either contain one element, and nothing else apart from that, or it may have no element but possibly something unrelated.}