# How to write profunctor lenses

– and prisms

A profunctor optic has the following general form

``type LensLike p s t a b = p a b -> p s t``

Compare this with the van Laarhoven version

``type LensLikeVL f s t a b = (a -> f b) -> s -> f t``

The way we write lenses in profunctor form is to see `p a b` as some sort of converter from `a` to `b` and transform it to a converter from `s` to `t`. Lenses work with the profunctor typeclass `Strong`, defined by the following method:

``first' :: p a b -> p (a, c) (b, c)``

That allows a converter from `a` to `b` to be transformed into a converter from “`a` and some stuff” “`b` and some stuff”, that is any product containing an `a` that can be “converted to” a `b`! That means we can write a lens (in terms of `second'` rather than `first'` since it makes the sequel slightly more convenient) by splitting `s` into “`a` and some stuff” and feeding it into one end of the `Strong` profunctor. Then we assemble `t` on the other end from “`b` and some stuff”.

``````_1 :: Strong p => p a a' -> p (a, b, c) (a', b, c)
_1 = dimap (\(a, b, c) -> ((b, c), a)) (\((b, c), a) -> (a, b, c)) . second'``````

In fact there are different representations for “stuff”. The stuff could even have been the assembling function itself:

``````_1' :: Strong p => p a a' -> p (a, b, c) (a', b, c)
_1' = dimap (\(a, b, c) -> (\a' -> (a', b, c), a)) (\(b_c, a) -> b_c a) . second'``````

This suggests the following, which turns out to be a way of creating a profunctor lens from a getter/setter:

``````lens :: Strong p => (s -> (b -> t, a)) -> p a b -> p s t
lens f = dimap f (uncurry (\$)) . second'``````

Prisms work with the profunctor typeclass `Choice`, defined by the following method:

``left' :: p a b -> p (Either a c) (Either b c)``

This time we don’t use “`a` and some stuff” but instead “`a` or some stuff”.

``````_Just :: Choice p => p a a' -> p (Maybe a) (Maybe a')
_Just = dimap (\case {Nothing -> Right (); Just a -> Left a})
(\case {Left a' -> Just a'; Right () -> Nothing})
. left'``````

As before there’s an equivalent way of presenting this

``````_Just' :: Choice p => p a a' -> p (Either a t) ((a' -> t) -> t)
_Just' = rmap (\case {Left a' -> (\$ a'); Right t -> const t})
. left'``````

which leads to a way of creating a prism from a match and constructor.

``````prism :: Choice p
=> (s -> Either a t) -> (b -> t) -> p a b -> p s t
prism f g = dimap f (\case {Left a' -> g a'; Right t -> t}) . left'``````