– 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)
= dimap (\(a, b, c) -> ((b, c), a)) (\((b, c), a) -> (a, b, c)) . second' _1
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)
= dimap (\(a, b, c) -> (\a' -> (a', b, c), a)) (\(b_c, a) -> b_c a) . second' _1'
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
= dimap f (uncurry ($)) . second' lens f
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')
= dimap (\case {Nothing -> Right (); Just a -> Left a})
_Just 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)
= rmap (\case {Left a' -> ($ a'); Right t -> const t})
_Just' . 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
= dimap f (\case {Left a' -> g a'; Right t -> t}) . left' prism f g