packages feed

proarrow-0.1.0.0: src/Proarrow/Profunctor/Instance/Day.hs

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

-- | Day convolution of profunctors: @'Day' p q@ convolves @p@ and @q@ along the tensors of the source and
-- target categories, with unit 'DayUnit' and internal hom 'DayExp'. Monoidal profunctors are closed under
-- it, and it preserves 'Procomonad's.
module Proarrow.Profunctor.Instance.Day where

import Proarrow.Category.Instance.Nat (Nat (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Monoidal
  ( Monoidal (..)
  , MonoidalProfunctor (..)
  , SymMonoidal (..)
  , swap
  , swapInner'
  , unitObj
  )
import Proarrow.Category.Monoidal.Closed (Closed (..))
import Proarrow.Category.Monoidal.CopyDiscard (CopyDiscard (..))
import Proarrow.Category.Monoidal.Distributive (Distributive (..))
import Proarrow.Category.Monoidal.Strength (MonStrong, first', second')
import Proarrow.Core
  ( CAT
  , CategoryOf (..)
  , Profunctor (..)
  , Promonad (..)
  , lmap
  , obj
  , rmap
  , src
  , tgt
  , (//)
  , type (+->)
  )
import Proarrow.Functor (Functor (..))
import Proarrow.Monoid (CocommutativeComonoid, CommutativeMonoid, Comonoid (..), Monoid (..), Supplies)
import Proarrow.Object (pattern Objs)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Coproduct ((:+:) (..))
import Proarrow.Promonad (Procomonad (..))

-- | The unit of 'Day' convolution: a pair of arrows through the monoidal units.
data DayUnit a b where
  DayUnit :: a ~> Unit -> Unit ~> b -> DayUnit a b

instance (CategoryOf j, CategoryOf k) => Profunctor (DayUnit :: j +-> k) where
  dimap l r (DayUnit f g) = DayUnit (f . l) (r . g)
  r \\ DayUnit f g = r \\ f \\ g

type Day :: (j +-> k) -> (j +-> k) -> j +-> k

-- | The Day convolution on profunctors.
data Day p q a b where
  Day :: forall c d e f p q a b. a ~> c ** e -> p c d -> q e f -> d ** f ~> b -> Day p q a b

day :: (Monoidal j, Monoidal k, Profunctor (p :: j +-> k), Profunctor q) => p c d -> q e f -> Day p q (c ** e) (d ** f)
day p q = Day (src p ** src q) p q (tgt p ** tgt q)

instance (Profunctor p, Profunctor q) => Profunctor (Day p q) where
  dimap l r (Day f p q g) = Day (lmap l f) p q (rmap r g)
  r \\ Day f _ _ g = r \\ f \\ g

instance (Procomonad p, Procomonad q, Monoidal k) => Procomonad (Day p q :: k +-> k) where
  proextract (Day f p q g) = g . (proextract p ** proextract q) . f
  produplicate (Day f p q g) = case (produplicate p, produplicate q) of
    (p' :.: p'', q' :.: q'') -> let bb = tgt p' ** tgt q' in Day f p' q' bb :.: Day bb p'' q'' g

instance (Profunctor p) => Functor (Day p) where
  map (Prof n) = Prof \(Day f p q g) -> Day f p (n q) g

instance Functor Day where
  map (Prof n) = Nat (Prof \(Day f p q g) -> Day f (n p) q g)

instance (SymMonoidal j, SymMonoidal k, MonoidalProfunctor p, MonoidalProfunctor q) => MonoidalProfunctor (Day p q :: j +-> k) where
  one = Day leftUnitorInv one one leftUnitor \\ unitObj @j \\ unitObj @k
  Day f1 p1 q1 g1 ** Day f2 p2 q2 g2 =
    let f = swapInner' (src p1) (src q1) (src p2) (src q2) . (f1 ** f2)
        g = (g1 ** g2) . swapInner' (tgt p1) (tgt p2) (tgt q1) (tgt q2)
    in Day f (p1 ** p2) (q1 ** q2) g

instance (Monoidal j, Monoidal k) => MonoidalProfunctor (Prof :: CAT (j +-> k)) where
  one = id
  Prof m ** Prof n = Prof \(Day f p q g) -> Day f (m p) (n q) g

instance (Monoidal j, Monoidal k) => Monoidal (j +-> k) where
  type Unit = DayUnit
  type p ** q = Day p q
  withOb2 r = r
  leftUnitor = Prof \(Day f (DayUnit h i) q g) -> dimap (leftUnitor . (h ** src q) . f) (g . (i ** tgt q) . leftUnitorInv) q \\ q
  leftUnitorInv = Prof \q -> Day leftUnitorInv (DayUnit one one) q leftUnitor \\ q
  rightUnitor = Prof \(Day f p (DayUnit h i) g) -> dimap (rightUnitor . (src p ** h) . f) (g . (tgt p ** i) . rightUnitorInv) p \\ p
  rightUnitorInv = Prof \p -> Day rightUnitorInv p (DayUnit one one) rightUnitor \\ p
  associator = Prof \(Day @_ @_ @e1 @f1 f1 (Day @c2 @d2 @e2 @f2 f2 p2@Objs q2@Objs g2) q1@Objs g1) ->
    Day
      (associator @_ @c2 @e2 @e1 . (f2 ** src q1) . f1)
      p2
      (day q2 q1)
      (g1 . (g2 ** tgt q1) . associatorInv @_ @d2 @f2 @f1)
  associatorInv = Prof \(Day @c1 @d1 f1 p1@Objs (Day @c2 @d2 @e2 @f2 f2 p2@Objs q2@Objs g2) g1) ->
    Day
      (associatorInv @_ @c1 @c2 @e2 . (src p1 ** f2) . f1)
      (day p1 p2)
      q2
      (g1 . (tgt p1 ** g2) . associator @_ @d1 @d2 @f2)

instance (SymMonoidal j, SymMonoidal k) => SymMonoidal (j +-> k) where
  swap = Prof \(Day @c @d @e @f f p q g) -> Day (swap @_ @c @e . f) q p (g . swap @_ @f @d) \\ p \\ q

instance (Profunctor p, MonoidalProfunctor p) => Monoid p where
  mempty = Prof \(DayUnit f g) -> dimap f g one
  mappend = Prof \(Day f p q g) -> dimap f g (p ** q)

instance (SymMonoidal j, CopyDiscard k, Supplies CommutativeMonoid j, Profunctor p) => Comonoid (p :: j +-> k) where
  counit = Prof \p -> p // DayUnit discard mempty
  comult = Prof \p -> p // Day copy p p mappend
instance (SymMonoidal j, CopyDiscard k, Supplies CommutativeMonoid j, Profunctor p) => CocommutativeComonoid (p :: j +-> k)

instance (SymMonoidal j, CopyDiscard k, Supplies CommutativeMonoid j) => CopyDiscard (j +-> k)

instance (Monoidal j, Monoidal k) => Distributive (j +-> k) where
  distL = Prof \(Day l a bc r) -> case bc of
    InjL b -> InjL (Day l a b r)
    InjR c -> InjR (Day l a c r)
  distR = Prof \(Day l ab c r) -> case ab of
    InjL a -> InjL (Day l a c r)
    InjR b -> InjR (Day l b c r)
  absorbL = Prof \case {}
  absorbR = Prof \case {}

duoidal
  :: (Monoidal j, Profunctor (p :: j +-> k), Profunctor p', Profunctor q, Profunctor q')
  => (p :.: p') `Day` (q :.: q') ~> (p `Day` q) :.: (p' `Day` q')
duoidal = Prof \(Day f (p :.: p') (q :.: q') g) -> let b = tgt p ** tgt q in Day f p q b :.: Day b p' q' g

-- | The internal hom of 'Day' convolution, making the category of profunctors 'Closed'.
data DayExp p q a b where
  DayExp
    :: forall p q a b. (Ob a, Ob b) => (forall c d e f. e ~> a ** c -> b ** d ~> f -> p c d -> q e f) -> DayExp p q a b

instance (Monoidal j, Monoidal k, Profunctor (p :: j +-> k), Profunctor q) => Profunctor (DayExp p q) where
  dimap l r (DayExp n) = l // r // DayExp \f g p -> n ((l ** src p) . f) (g . (r ** tgt p)) p
  r \\ DayExp n = r \\ n

instance (Monoidal j, Monoidal k, Profunctor (p :: j +-> k)) => Functor (DayExp p) where
  map (Prof n) = Prof \(DayExp m) -> DayExp \f g p -> n (m f g p)

instance (Monoidal j, Monoidal k) => Closed (j +-> k) where
  type p ~~> q = DayExp p q
  withObExp r = r
  curry (Prof n) = Prof \p -> p // DayExp \f g q -> n (Day f p q g)
  apply = Prof \(Day f (DayExp h) q g) -> h f g q
  (^^^) (Prof n) (Prof m) = Prof \(DayExp k) -> DayExp \f g p -> n (k f g (m p))

multDayExp
  :: (SymMonoidal j, SymMonoidal k, Profunctor (p :: j +-> k), Profunctor q, Profunctor p', Profunctor q')
  => (p ~~> q) `Day` (p' ~~> q') ~> (p `Day` p') ~~> (q `Day` q')
multDayExp = Prof \(Day @c @d @e @f g (DayExp pq) (DayExp pq') h) ->
  g //
    h //
      DayExp
        ( \l r (Day i p p' j) ->
            p //
              p' //
                let c = obj @c; d = obj @d; e = obj @e; f = obj @f; c' = src p; d' = tgt p; e' = src p'; f' = tgt p'
                in Day
                     (swapInner' c e c' e' . (g ** i) . l)
                     (pq (c ** c') (d ** d') p)
                     (pq' (e ** e') (f ** f') p')
                     (r . (h ** j) . swapInner' d d' f f')
        )

-- Day p q :: j +-> k can be Strong in multiple ways:
-- 1. Either p or q is strong, and we have: Act a (b ** c) ~ (Act a b) ** c.
-- 2. Or p and q are both strong, and we have: Act a (b ** c) ~ (Act a b) ** (Act a c)

day2comp :: (MonStrong (p :: k +-> k), MonStrong q, Monoidal k) => Day p q ~> p :.: q
day2comp = Prof \(Day @_ @d @e f p q g) -> lmap f (first' @e p) :.: rmap g (second' @d q) \\ p \\ q