packages feed

proarrow-0.1.0.0: src/Proarrow/Optic/Setter.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | The __setter__: the weakest write-side optic, applying a morphism to every focus ('SetterFl'
-- \/ 'overP'). It sits at the write-only top of the subtyping lattice alongside
-- 'Proarrow.Optic.Fold.Fold', so it has no builder of its own ('Proarrow.Optic.convert' a stronger
-- optic). Its canonical eliminator is 'over', via the generic 'ExOptic' carrier, with 'set',
-- '(%~)' and '(.~)' as shorthands.
--
-- This module also hosts the 'SetterFl' instance of the tensor-action witness pair
-- @'Rep'@\/@'Corep'@ @('ActionAt' 'Tensor' a)@, shared by "Proarrow.Optic.MonoidalTraversal" and
-- "Proarrow.Optic.Tracer".
module Proarrow.Optic.Setter where

import Data.Kind (Type)
import Prelude (const)
import Prelude qualified as P

import Proarrow.Category.Instance.Kleisli (KLEISLI (..), Kleisli (..))
import Proarrow.Category.Monoidal (Monoidal, MonoidalProfunctor (..), Tensor)
import Proarrow.Category.Monoidal.Action (ActionAt)
import Proarrow.Category.Monoidal.Closed (Closed (..), Exp)
import Proarrow.Colimit.BinaryCoproduct (Coproduct, HasCoproducts, right)
import Proarrow.Core (CategoryOf (..), Profunctor (..), Promonad (..), obj, (\\), type (+->))
import Proarrow.Functor (Prelude (..))
import Proarrow.Limit.BinaryProduct (HasBinaryProducts, Product, second)
import Proarrow.Optic (ExOptic, FLAVOR, Optic, Prostrong (..), withLegs)
import Proarrow.Profunctor.Corepresentable (Corep (..), Corepresentable (..))
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Identity (Id (..))
import Proarrow.Profunctor.Instance.Star (Star, unStar, pattern Star)
import Proarrow.Profunctor.Representable (CorepStar (..), Rep (..), RepCostar (..), Representable (..))

-- | A setter can only apply a pure function to the @a@'s it can see. It can neither view nor
-- fold them. A traversal is both a setter and a fold.
type SetterFl :: forall {k}. FLAVOR k k
class (Profunctor p, Profunctor q) => SetterFl (p :: k +-> k) (q :: k +-> k) where
  overP :: p s a -> q b t -> (a ~> b) -> (s ~> t)

-- | Every /representable/ residual is a setter: map the focus through the residual functor with
-- 'repMap'. This needs only 'Representable' @t@, not
-- 'Proarrow.Category.Monoidal.Distributive.Traversable', which is why
-- 'Proarrow.Optic.Setter.Setter' sits at the top of the lattice: functoriality of the residual is
-- all @over@ ever uses. Richer optics ('Proarrow.Optic.Lens.Lens',
-- 'Proarrow.Optic.Traversal.Traversal', ...) are this witness plus extra algebra on @t@.
instance (Representable t) => SetterFl (t :: k +-> k) (RepCostar t) where
  overP l (RepCostar r) f = r . repMap @t f . index l

instance (HasBinaryProducts k, Ob (s :: k)) => SetterFl (Rep (Product s)) (Corep (Product s)) where
  overP (Rep p) (Corep q) f = q . second @s f . p
instance (HasCoproducts k, Ob t) => SetterFl (Rep (Coproduct t) :: k +-> k) (Corep (Coproduct t)) where
  overP (Rep p) (Corep q) f = q . right @t f . p
instance (CategoryOf k) => SetterFl (Id :: k +-> k) (Id :: k +-> k) where
  overP (Id l) (Id r) f = r . f . l
instance (SetterFl f g, SetterFl f' g') => SetterFl (f :.: f') (g' :.: g) where
  overP (f :.: f') (g' :.: g) = overP @f @g f g . overP @f' @g' f' g'

-- | Dually, every /corepresentable/ residual is a setter: map with 'corepMap'. Needs only
-- 'Corepresentable' @t@, not 'Proarrow.Category.Monoidal.Distributive.Cotraversable'.
instance (Corepresentable t) => SetterFl (CorepStar t) t where
  overP (CorepStar l) co f = coindex co . corepMap @t f . l

-- | The grate witness is a setter witness: map under the exponential. The 'Closed' structure
-- this needs rides in the instance context, not in @overP@'s own (weaker) constraint.
instance (Closed k, Ob (m :: k)) => SetterFl (Rep (Exp m) :: k +-> k) (Corep (Exp m)) where
  overP (Rep sm) (Corep mbt) f = mbt . (f ^^^ obj @m) . sm \\ f

-- | The tensor-action witness pair @'Rep'@\/@'Corep'@ @('ActionAt' 'Tensor' a)@: the focus @x@
-- sits inside @a ** x@ with the residual @a@ carried on the left (legs @s ~> a ** x@ and
-- @a ** x ~> t@). It is a setter witness by mapping under the tensor, and the tensor-strength
-- generator for the free traversal profunctor (see "Proarrow.Optic.MonoidalTraversal"); read the
-- other way round it is the tracer witness (see "Proarrow.Optic.Tracer").
instance (Monoidal k, Ob (a :: k)) => SetterFl (Rep (ActionAt Tensor a) :: k +-> k) (Corep (ActionAt Tensor a)) where
  overP (Rep h) (Corep i) f = i . (obj @a ** f) . h

type Setter (s :: k) (t :: k) a b = Optic (Prostrong SetterFl) s t a b
type Setter' s a = Setter s s a a

-- | Map over any optic that can act as a setter, in either encoding: run it at its witness pair
-- ('ExOptic' 'SetterFl', via 'withLegs') and apply 'overP'.
over
  :: forall {k} c (s :: k) (t :: k) a b
   . (CategoryOf k, (Ob a, Ob b) => c (ExOptic SetterFl a b))
  => Optic c s t a b -> (a ~> b) -> (s ~> t)
over o f = withLegs @SetterFl o \ @p @q p q -> overP @p @q p q f

-- | Apply a function through a concrete, @Type@-level 'Setter'.
infixl 8 %~

(%~) :: (c (ExOptic SetterFl a b)) => Optic c (s :: Type) t a b -> (a -> b) -> (s -> t)
(%~) = over

-- | Replace the focus\/foci of a concrete, @Type@-level 'Setter' with a constant value.
infixl 8 .~

(.~) :: (c (ExOptic SetterFl a b)) => Optic c (s :: Type) t a b -> b -> (s -> t)
l .~ b = l %~ const b

-- | Named version of '(.~)'.
set :: (c (ExOptic SetterFl a b)) => Optic c (s :: Type) t a b -> b -> (s -> t)
set = (.~)

-- | Monadically replace the focus\/foci of a 'Setter' in the Kleisli category of @m@ with a
-- constant value, ignoring the old contents entirely.
mupdate
  :: forall m s t a b
   . (P.Monad m)
  => Setter (KL s :: KLEISLI (Star (Prelude m))) (KL t) (KL a) (KL b) -> b -> s -> m t
mupdate l b s = unPrelude (unStar (unKleisli (over l (Kleisli (Star (\_ -> Prelude (P.return b)))))) s)