I'm currently working through tutorials for the Haskell lens package, so I can better understand the underlying mathematical foundations. And I was working through Prisms.
type Prism s t a b = forall p f. (Applicative f, Choice p) => p a (f b) -> p s (f t)
We find that a prism is effectively b -> t and s -> Either t a. We can exhibit this isomorphism directly.
-- Note: Stripping out the APrism / AReview type synonyms used
-- in Control.Lens for simplicity.
prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
prism bt seta = dimap seta (either pure (fmap bt)) . right'
matching :: Prism s t a b -> s -> Either t a
matching aprism s = let Market _ f = aprism (Market Identity Right) in
left runIdentity $ f s
review :: Prism s t a b -> b -> t
review areview = runIdentity . unTagged . areview . Tagged . Identity
(Note: Sources for Tagged and Market)
At this point, I had the thought "Hey, Choice is kind of just Strong but on Either rather than (,)". So I wanted to know what sort of prism-like thing you get if you replace Choice with Strong. I was expecting to write
type ProductPrism s t a b = forall p f. (Applicative f, Strong p) => p a (f b) -> p s (f t)
and find that ProductPrism s t a b is isomorphic to (b -> t, s -> (t, a)). But as I went to write these isomorphisms, I discovered two things:
b -> t part is actually irrelevant here (you don't need it in order to construct a ProductPrism)pure, so we can get by with Apply.So I ended up with this.
type ProductPrism s t a b = forall p f. (Apply f, Strong p) => p a (f b) -> p s (f t)
-- Construct ProductPrism from (s -> (t, a))
productPrism :: (s -> (t, a)) -> ProductPrism s t a b
productPrism sta = dimap sta (\(t, fb) -> t <$ fb) . second'
-- Consume ProductPrism into (s -> (t, a))
split :: ProductPrism (Semi.First a) s t a -> s -> (t, a)
split pprism s = go $ pprism (\a -> (Semi.First a, a)) s
where go (Semi.First a, t) = (t, a)
where Semi.First is the semigroup First (not the monoid version).
So ProductPrism s t a b, as I've defined it, is just s -> (t, a) with a comically unused b argument.
My question is: Is this type useful? Does this functional reference have a well-known name like Lens or Prism or Traversal? Does it give us any useful abstractions like many of the other optics do?
I think you've gotten the choice of the c parameter in Strong wrong. It shouldn't be c ~ t; it should be c ~ s. So, my read is that you're looking for the following:
type PPrism s t a b = forall p f. (Functor f, Strong p) => p a (f b) -> p s (f t)
pprism :: (s -> a) -> (b -> s -> t) -> PPrism s t a b
pprism sa bst = dimap (\s -> (s, sa s)) (\(s, fb) -> flip bst s <$> fb) . second'
unprism :: PPrism s t a b -> ((s -> a), (b -> s -> t))
unprism pp = (sa, bst)
where sa = getConst . pp (Const . id)
bst b = runIdentity . pp (const (Identity b))
In short, this is really just a lens. (Or, if you choose Applicative f instead, a traversal.)
That makes sense, right? A Prism is an optical generalization of a sum type based on a profunctor with Choice. A Lens is an optical generalization of a product type based on a profunctor with Strong, though we usually just define it with p ~ (->).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With