distributors-0.4.0.0: src/Control/Lens/Diopter.hs
{- |
Module : Control.Lens.Diopter
Description : diopters
Copyright : (C) 2026 - Eitan Chatav
License : BSD-style (see the file LICENSE)
Maintainer : Eitan Chatav <eitan.chatav@gmail.com>
Stability : provisional
Portability : non-portable
-}
module Control.Lens.Diopter
( -- * Diopter
Diopter
, ADiopter
-- * Combinators
, diopter
, withDiopter
, cloneDiopter
, mapDiopter
, optioned
, manied
, homogenized
-- * Dioptrice
, Dioptrice (..), runDioptrice
) where
import Control.Lens
import Control.Lens.Internal.Profunctor
import Data.Profunctor.Distributor
import Data.Traversable.Homogeneous
import Data.Void
import GHC.Generics
{- | `Diopter`s are an optic that generalizes
`Control.Lens.Bifocal.Bifocal`s and `Control.Lens.Traversal.Traversal`s.
Every `Control.Lens.Iso.Iso` and `Control.Lens.Monocle` is a `Diopter`.
`Diopter`s are isomorphic to `Dioptrice`s.
-}
type Diopter s t a b = forall p f.
(Distributor p, Applicative f)
=> p a (f b) -> p s (f t)
{- | `ADiopter` is monomorphically a `Diopter`. -}
type ADiopter s t a b =
Dioptrice a b a (Identity b) -> Dioptrice a b s (Identity t)
{- | Build a `Diopter`. -}
diopter :: Homogeneous h => (s -> h a) -> (h b -> t) -> Diopter s t a b
diopter f g = unwrapPafb . runDioptrice (Dioptrice f g) . WrapPafb
{- | Convert `ADiopter` to the pair of functions that characterize it. -}
withDiopter
:: ADiopter s t a b
-> (forall h. Homogeneous h => (s -> h a) -> (h b -> t) -> r)
-> r
withDiopter dio k = case runIdentity <$> dio (Identity <$> Dioptrice Par1 unPar1) of
Dioptrice f g -> k f g
{- | Action of `ADiopter` on `Distributor`s. -}
mapDiopter :: Distributor p => ADiopter s t a b -> p a b -> p s t
mapDiopter dio = withDiopter dio $ \f g -> dimap f g . homogeneously
{- | Clone `ADiopter` so that you can reuse the same
monomorphically typed `Diopter` for different purposes.
-}
cloneDiopter :: ADiopter s t a b -> Diopter s t a b
cloneDiopter dio = withDiopter dio diopter
{- | One or none. -}
optioned :: Diopter (Maybe a) (Maybe b) a b
optioned = unwrapPafb . optionalP . WrapPafb
{- | Zero or more. -}
manied :: Diopter [a] [b] a b
manied = unwrapPafb . manyP . WrapPafb
{- | Build a `Diopter` from a `Homogeneous`
countable sum of countable products.
prop> traverse = homogenized
prop> homogenized = ditraversed
-}
homogenized :: Homogeneous t => Diopter (t a) (t b) a b
homogenized = unwrapPafb . homogeneously . WrapPafb
{- | A `Dioptrice` provides efficient access
to some pair of functions that make up a `Diopter`.
-}
data Dioptrice a b s t where
Dioptrice
:: Homogeneous h
=> (s -> h a)
-> (h b -> t)
-> Dioptrice a b s t
instance Profunctor (Dioptrice a b) where
dimap f g (Dioptrice sa bt) = Dioptrice (sa . f) (g . bt)
instance Functor (Dioptrice a b s) where fmap = rmap
instance Applicative (Dioptrice a b s) where
pure t = Dioptrice (const U1) (const t)
Dioptrice fx gx <*> Dioptrice fy gy = Dioptrice
(\s -> fx s :*: fy s)
(\(h :*: g) -> gx h (gy g))
instance Distributor (Dioptrice a b) where
zeroP = Dioptrice absurd ridiculous
where
ridiculous :: V1 b -> x
ridiculous = (\case)
Dioptrice fx gx >+< Dioptrice fy gy = Dioptrice
(either (L1 . fx) (R1 . fy))
(\case {L1 h -> Left (gx h); R1 j -> Right (gy j)})
optionalP (Dioptrice f g) = Dioptrice
(Comp1 . fmap f)
(fmap g . unComp1)
manyP (Dioptrice f g) = Dioptrice
(Comp1 . fmap f)
(fmap g . unComp1)
{- | Run a `Dioptrice` on a `Distributor`. -}
runDioptrice
:: Distributor p
=> Dioptrice a b s t
-> p a b -> p s t
runDioptrice (Dioptrice f g) = dimap f g . homogeneously