packages feed

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

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

-- | The category @'LIST' k@ of lists of objects of @k@, whose arrows are componentwise lists of arrows.
-- @'List' p@ lifts a profunctor @p@ componentwise to lists. Lists of objects are the arity-indexing used by
-- promonoidal categories ("Proarrow.Category.Promonoidal") and by cones and cocones.
module Proarrow.Profunctor.Instance.List where

import Proarrow.Category.Enriched.Dagger (DaggerProfunctor (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Monoidal (Monoidal (..), MonoidalProfunctor (..), Strictly (..))

-- import Proarrow.Category.Monoidal.Action (MonoidalAction (..), Strong (..))
import Proarrow.Category.Monoidal.Strictified qualified as Str
import Proarrow.Core (CategoryOf (..), Is, Profunctor (..), Promonad (..), UN, type (+->))
import Proarrow.Functor (Functor (..))

-- import Proarrow.Profunctor.Instance.Identity (Id (..))
import Proarrow.Profunctor.Representable (Representable (..))

type data LIST k = L [k]

-- | Lifts @p@ componentwise to lists: an arrow between equal-length lists of objects is a list of
-- @p@-arrows.
type List :: (j +-> k) -> LIST j +-> LIST k
data List p as bs where
  Nil :: List p (L '[]) (L '[])
  Cons :: (Str.IsList as, Str.IsList bs) => p a b -> List p (L as) (L bs) -> List p (L (a ': as)) (L (b ': bs))

mkCons :: (Profunctor p) => p a b -> List p (L as) (L bs) -> List p (L (a ': as)) (L (b ': bs))
mkCons f fs = Cons f fs \\ fs

foldList :: (MonoidalProfunctor p) => List p as bs -> p (Str.Fold (UN L as)) (Str.Fold (UN L bs))
foldList Nil = one
foldList (Cons p Nil) = p
foldList (Cons p ps@Cons{}) = p ** foldList ps

instance Functor List where
  map (Prof n) = Prof \case
    Nil -> Nil
    Cons p ps -> Cons (n p) (unProf (map (Prof n)) ps)

-- | The category of lists of arrows.
instance (CategoryOf k) => CategoryOf (LIST k) where
  type (~>) = List (~>)
  type Ob as = (Is L as, Str.IsList (UN L as))

instance (Promonad p) => Promonad (List p) where
  id @(L bs) = case Str.sList @bs of
    Str.SNil -> Nil
    Str.SSing -> Cons id Nil
    Str.SCons -> Cons id id
  Nil . Nil = Nil
  Cons f fs . Cons g gs = Cons (f . g) (fs . gs)

instance (Profunctor p) => Profunctor (List p) where
  dimap Nil Nil Nil = Nil
  dimap (Cons l ls) (Cons r rs) (Cons f fs) =
    Cons (dimap l r f) (dimap ls rs fs)
  dimap Nil Cons{} fs = case fs of {}
  dimap Cons{} Nil fs = case fs of {}
  r \\ Nil = r
  r \\ Cons f Nil = r \\ f
  r \\ Cons f fs@Cons{} = r \\ f \\ fs

-- | The free monoidal profunctor on a profunctor.
instance (Profunctor p) => MonoidalProfunctor (List p) where
  one = Nil
  Nil ** Nil = Nil
  Nil ** gs@Cons{} = gs
  Cons f fs ** Nil = mkCons f (fs ** Nil)
  Cons f fs ** Cons g gs = mkCons f (fs ** Cons g gs)

-- | The free monoidal category on a category.
instance (CategoryOf k) => Monoidal (LIST k) where
  type Unit = L '[]
  type p ** q = L (UN L p Str.++ UN L q)
  withOb2 @(L as) @(L bs) r = Str.withIsList2 @as @bs r
  associator @as @bs @cs = associatorDefault @as @bs @cs
  associatorInv @as @bs @cs = associatorDefault @as @bs @cs

instance (Representable p) => Representable (List p) where
  type List p % L '[] = L '[]
  type List p % L (a ': as) = L ((p % a) ': UN L (List p % L as))
  index Nil = Nil
  index (Cons p Nil) = Cons (index @p p) Nil
  index (Cons p ps@Cons{}) = mkCons (index @p p) (index @(List p) ps)
  tabulate @(L b) Nil = case Str.sList @b of Str.SNil -> Nil
  tabulate @(L b) (Cons f Nil) = case Str.sList @b of Str.SSing -> Cons (tabulate @p f) Nil
  tabulate @(L b) (Cons f fs@Cons{}) = case Str.sList @b of Str.SCons -> Cons (tabulate @p f) (tabulate @(List p) fs)
  repMap Nil = Nil
  repMap (Cons f Nil) = Cons (repMap @p f) Nil
  repMap (Cons f fs@Cons{}) = mkCons (repMap @p f) (repMap @(List p) fs)

instance (DaggerProfunctor p) => DaggerProfunctor (List p) where
  dagger Nil = Nil
  dagger (Cons f fs) = Cons (dagger f) (dagger fs)