packages feed

comonad-coactions-0.1.0.1: src/Control/Comonad/Coaction.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_HADDOCK show-extensions #-}

{-|
Module      : Control.Comonad.Coaction
Description : comonad coactions
Copyright   : © noiioiu
License     : LGPL-2
Maintainer  : noiioiu@cocaine.ninja
Stability   : experimental

Given a comonad \(W\) on a category \(\mathcal{D}\) with counit \(\varepsilon\) and
comultiplication \(\Delta\) and a functor \(F\) from \(\mathcal{C}\) to \(\mathcal{D}\),
a left (or outer) comonad coaction of \(W\) on \(F\) is a natural
transformation \(\Sigma: F \to W \circ F\) such that the following two laws hold:

* \((\varepsilon \circ F) \cdot \Sigma  = \mathrm{id}_F\)
* \((\Delta \circ F) \cdot \Sigma   = (W \circ \Sigma) \cdot \Sigma\)

We also say that \(F\) is a left comodule over \(W\).  In the
case \(\mathcal{C} = \mathcal{D}\), a left comonad comodule is a left comonoid
comodule object in the category of endofunctors on \(\mathcal{C}\). Right
(or inner) comonad coactions are defined similarly.
-}
module Control.Comonad.Coaction
  ( LeftComodule (..),
    RightComodule (..),
    BiComodule (..),
    ComonadTransStack (..),
  )
where

import Control.Comonad (Comonad (..))
import Control.Comonad.Density (Density (..))
import Control.Comonad.Env.Class (ComonadEnv (..))
import Control.Comonad.Identity (Identity (..))
import Control.Comonad.Store.Class (ComonadStore (..))
import Control.Comonad.Traced.Class (ComonadTraced (..))
import Control.Comonad.Trans.Store (Store, store)
import Control.Comonad.Trans.Traced (Traced, traced)
import Control.Comonad.TransformerStack (ComonadTransStack (..))
import Control.Monad (liftM2)
import Data.Functor.Compose (Compose (..))
import Data.Functor.Rep (Representable (..))
import Data.Functor.Sum (Sum (..))

{-| Instances must satisfy the following laws:

* @'duplicate' '.' 'lduplicate' = 'fmap' 'lduplicate' '.' 'lduplicate'@

* @'extract' '.' 'lduplicate' = 'id'@
-}
class (Comonad w, Functor f) => LeftComodule w f where
  lduplicate ::
    f a ->
    -- | left comonad coaction
    w (f a)
  lduplicate = lextend id
  lextend :: (f a -> b) -> f a -> w b
  lextend f = fmap f . lduplicate
  {-# MINIMAL lduplicate | lextend #-}

{-| Instances must satisfy the following laws:

* @'fmap' 'duplicate' '.' 'rduplicate' = 'rduplicate' '.' 'rduplicate'@

* @'fmap' 'extract' '.' 'lduplicate' = 'id'@
-}
class (Comonad w, Functor f) => RightComodule w f where
  rduplicate ::
    f a ->
    -- | right comonad coaction
    f (w a)
  rduplicate = rextend id
  rextend :: (w a -> b) -> f a -> f b
  rextend f = fmap f . rduplicate
  {-# MINIMAL rduplicate | rextend #-}

{-| Given two comonads @r@ and @s@, an @(r, s)@ bicomodule is a functor that is a left comodule over @r@ and a right comodule over @s@, where the two coactions are compatible.
  Instances must satisfy the following law in addition to the laws for @'LeftComodule'@ and @'RightComodule'@:

* @'lduplicate' '.' 'rduplicate' = 'fmap' 'rduplicate' '.' 'lduplicate' = 'biduplicate'@
-}
class (LeftComodule r f, RightComodule s f) => BiComodule r s f where
  biduplicate ::
    f a ->
    -- | two-sided comonad coaction
    r (f (s a))
  biduplicate = lduplicate . rduplicate

instance {-# INCOHERENT #-} (Comonad w) => LeftComodule Identity w where
  lduplicate = Identity

instance {-# INCOHERENT #-} (Comonad w) => RightComodule Identity w where
  rduplicate = fmap Identity

instance {-# INCOHERENT #-} (Comonad w) => BiComodule Identity Identity w where
  biduplicate = Identity . fmap Identity

instance {-# INCOHERENT #-} (Comonad w, Functor f, LeftComodule w v) => LeftComodule w (Compose v f) where
  lduplicate = fmap Compose . lduplicate . getCompose
  lextend f = lextend (f . Compose) . getCompose

instance {-# INCOHERENT #-} (Comonad w, Functor f, RightComodule w v) => RightComodule w (Compose f v) where
  rduplicate = Compose . fmap rduplicate . getCompose
  rextend f = Compose . fmap (rextend f) . getCompose

instance {-# INCOHERENT #-} (Comonad s, Comonad t, Functor f, LeftComodule s u, RightComodule t v) => BiComodule s t (Compose u (Compose f v))

{-| Proof that @f@ is always a left comodule over @t'Density' f@:

  * @   'duplicate' ('lduplicate' w)
      = 'duplicate' ('Density' 'id' w)
      = 'Density' ('Density' 'id') w
      = 'Density' 'lduplicate' w
      = 'fmap' 'lduplicate' ('Density' 'id' w)
      = 'fmap' 'lduplicate' ('lduplicate' w)@

  * @'extract' ('lduplicate' w) = 'extract' ('Density' 'id' w) = w@
-}
instance (Functor f) => LeftComodule (Density f) f where
  lduplicate = Density id

instance {-# INCOHERENT #-} (Comonad q, Comonad w, ComonadTransStack w q) => LeftComodule w q where
  lduplicate = lowerStack . duplicate
  lextend f = lowerStack . extend f

instance {-# INCOHERENT #-} (Comonad q, Comonad w, ComonadTransStack w q) => RightComodule w q where
  rduplicate = lextend lowerStack
  rextend f = extend $ f . lowerStack

instance {-# INCOHERENT #-} (Comonad q, Comonad w, ComonadTransStack w q) => BiComodule w w q

instance {-# INCOHERENT #-} (ComonadStore s w) => LeftComodule (Store s) w where
  lduplicate = liftM2 store (flip peek) pos . duplicate
  lextend f = liftM2 store (flip peek) pos . extend f

-- instance {-# INCOHERENT #-} (ComonadStore s w) => RightComodule (Store s) w where
--   rduplicate = extend $ liftM2 store (flip peek) pos
--   rextend f = extend $ f . liftM2 store (flip peek) pos

-- instance {-# INCOHERENT #-} (ComonadStore s w) => BiComodule (Store s) (Store s) w

instance {-# INCOHERENT #-} (ComonadEnv e w) => LeftComodule ((,) e) w where
  lduplicate = liftM2 (,) ask extract . duplicate
  lextend f = liftM2 (,) ask extract . extend f

instance {-# INCOHERENT #-} (ComonadEnv e w) => RightComodule ((,) e) w where
  rduplicate = extend $ liftM2 (,) ask extract
  rextend f = extend $ f . liftM2 (,) ask extract

instance {-# INCOHERENT #-} (ComonadEnv e w) => BiComodule ((,) e) ((,) e) w

instance {-# INCOHERENT #-} (ComonadTraced m w, Monoid m) => LeftComodule (Traced m) w where
  lduplicate = traced . flip trace . duplicate
  lextend f = traced . flip trace . extend f

instance {-# INCOHERENT #-} (ComonadTraced m w, Monoid m) => RightComodule (Traced m) w where
  rduplicate = extend $ traced . flip trace
  rextend f = extend $ f . traced . flip trace

instance {-# INCOHERENT #-} (ComonadTraced m w, Monoid m) => BiComodule (Traced m) (Traced m) w

instance {-# INCOHERENT #-} (LeftComodule w f, LeftComodule w g) => LeftComodule w (Sum f g) where
  lduplicate = \case
    InL x -> InL <$> lduplicate x
    InR x -> InR <$> lduplicate x
  lextend f = \case
    InL x -> lextend @w (f . InL) x
    InR x -> lextend @w (f . InR) x

instance {-# INCOHERENT #-} (RightComodule w f, RightComodule w g) => RightComodule w (Sum f g) where
  rduplicate = \case
    InL x -> InL $ rduplicate x
    InR x -> InR $ rduplicate x
  rextend f = \case
    InL x -> InL $ rextend @w f x
    InR x -> InR $ rextend @w f x

instance {-# INCOHERENT #-} (BiComodule w q f, BiComodule w q g) => BiComodule w q (Sum f g) where
  biduplicate = \case
    InL x -> InL <$> biduplicate x
    InR x -> InR <$> biduplicate x

instance (Representable f, Rep f ~ s) => RightComodule (Store s) f where
  rduplicate = tabulate . store . index