packages feed

functor-monad-0.1.1.0: src/FStrong.hs

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}

-- | 'FFunctor' with tensorial strength with respect to 'Day'.
module FStrong where

import Data.Coerce (coerce)

import Data.Functor.Day
import Data.Functor.Day.Curried

import FFunctor
import FMonad

import Data.Functor.Compose
import FFunctor.FCompose
import Data.Functor.Precompose ( Precompose(..) )
import Data.Functor.Bicompose ( Bicompose(..) )
import Control.Monad.Trans.Identity (IdentityT (..))
import Control.Monad.Trans.Reader (ReaderT (..))
import Control.Monad.Trans.State (StateT (..))
import Control.Monad.Trans.Writer (WriterT (..))
import Control.Comonad.Env (EnvT(..))
import Control.Comonad.Traced (TracedT(..))
import Control.Comonad.Store (StoreT (..))

-- | 'FFunctor' with tensorial strength with respect to 'Day'.
class FFunctor ff => FStrong ff where
  {-# MINIMAL fstrength | mapCurried #-}

  -- | Tensorial strength with respect to 'Day'.
  --
  -- 'fstrength' can be thought as a higher-order version of @strength@ function below.
  --
  -- @
  -- strength :: Functor f => (f a, b) -> f (a, b)
  -- strength (fa, b) = fmap (\a -> (a,b)) fa
  -- @
  --
  -- For the ordinary 'Functor', no additional type classes were needed to have @strength@ function,
  -- because it works for any @Functor f@. This is not the case for 'FFunctor' and 'fstrength'.
  --
  -- ==== Laws
  --
  -- These two equations must hold.
  --
  -- @
  -- ffmap 'elim2' . fstrength  = 'elim2'
  --       :: Day (ff g) 'Data.Functor.Identity.Identity' ~> ff g
  -- fstrength . 'trans1' fstrength = ffmap 'assoc' . fstrength . 'disassoc'
  --       :: Day (Day (ff g) h) k ~> ff (Day (Day g h) k))
  -- @
  --
  -- Alternatively, these diagrams must commute.
  --
  -- >                    fstrength
  -- >  ff g ⊗ Identity ----------->  ff (g ⊗ Identity)
  -- >            \                          |
  -- >             \                         |   ffmap elim2
  -- >              \                        |
  -- >        elim2  \                       v
  -- >                \---------------->   ff g
  --
  --
  -- >                     trans1 fstrength                      fstrength
  -- > (ff g ⊗ h) ⊗ k -------------------->  ff (g ⊗ h) ⊗ k ----------->  ff ((g ⊗ h) ⊗ k)
  -- >            |                                                                   ^
  -- >            | disassoc                                             ffmap assoc  |
  -- >            |                                                                   |
  -- >            v                           fstrength                               |
  -- >  ff g ⊗ (h ⊗ k) --------------------------------------------------->  ff (g ⊗ (h ⊗ k))
  --
  -- For readability, an infix operator @(⊗) was used instead of the type constructor @Day@.
  fstrength :: (Functor g) => Day (ff g) h ~> ff (Day g h)
  fstrength (Day ffg h op) =
    runCurried (mapCurried (unapplied h)) (fmap op ffg)

  -- | Internal 'ffmap'.
  --
  -- 'mapCurried' can be seen as @ffmap@ but by using "internal language" of
  -- \(\mathrm{Hask}^{\mathrm{Hask}}\), the category of @Functor@s.
  --
  -- @
  -- ffmap         :: (g ~> h)       ->  (ff g ~> ff h)
  -- mapCurried    :: (Curried g h)  ~>  (Curried (ff g) (ff h))
  -- @
  --
  -- @ffmap@ takes a natural transformations @(~>)@, in other words morphism in \(\mathrm{Hask}^{\mathrm{Hask}}\),
  -- and returns another @(~>)@. @ffmap@ itself is an usual function, which is an outsider for
  -- \(\mathrm{Hask}^{\mathrm{Hask}}\).
  --
  -- On the other hand, @mapCurried@ is a natural transformation @(~>)@ between @Curried _ _@,
  -- objects of \(\mathrm{Hask}^{\mathrm{Hask}}\).
  -- 
  -- The existence of 'mapCurried' is known to be equivalent to the existence of
  -- 'fstrength'.
  --
  -- ==== Laws
  --
  -- These equations must hold.
  --
  -- @
  -- mapCurried . 'Data.Functor.Day.Extra.unitCurried' = 'Data.Functor.Day.Extra.unitCurried'
  --     :: Identity ~> Curried (ff g) (ff g)
  -- mapCurried . 'Data.Functor.Day.Extra.composeCurried' = 'Data.Functor.Day.Extra.composeCurried' . trans1 mapCurried . trans2 mapCurried
  --     :: Day (Curried g h) (Curried h k) ~> Curried (ff g) (ff k)
  -- @
  mapCurried :: (Functor g, Functor h) => Curried g h ~> Curried (ff g) (ff h)
  mapCurried gh = Curried $ \ffg -> ffmap applied (fstrength (day ffg gh))

-- | 'fstrength' but from left
fstrength' :: (FStrong ff, Functor h) => Day g (ff h) ~> ff (Day g h)
fstrength' = ffmap swapped . fstrength . swapped

-- | Combine an applicative action(s) inside @ff@ to another action @h a@.
innerAp :: (FStrong ff, Applicative h) => ff h (a -> b) -> h a -> ff h b
innerAp ffh h = ffmap dap $ fstrength (day ffh h)

-- | Cf. 'Control.Monad.ap'
fap :: (FStrong mm, FMonad mm, Functor g, Functor h) => Day (mm g) (mm h) ~> mm (Day g h)
fap = fjoin . ffmap fstrength' . fstrength

instance FStrong IdentityT where
  fstrength = coerce

instance FStrong (Day f) where
  fstrength = disassoc

instance Functor f => FStrong (Curried f) where
  fstrength = toCurried (trans1 applied . assoc)

instance Functor f => FStrong (Compose f) where
  fstrength (Day (Compose fg) h op) = Compose (fmap (\g -> Day g h op) fg)

instance Functor f => FStrong (Precompose f) where
  fstrength (Day (Precompose gf) h op) = Precompose (Day gf h (\fa b -> fmap (flip op b) fa))

instance (Functor f, Functor f') => FStrong (Bicompose f f') where
  fstrength (Day (Bicompose fgf') h op) =
    Bicompose $
      fmap (\gf' -> Day gf' h (\fa b -> fmap (flip op b) fa)) fgf'

instance FStrong (ReaderT e) where
  fstrength (Day (ReaderT eg) h op) = ReaderT $ \e -> Day (eg e) h op

instance FStrong (WriterT m) where
  fstrength (Day (WriterT gm) h op) = WriterT $ Day gm h (\(b, m) c -> (op b c, m))

instance FStrong (StateT s) where
  -- StateT s = ReaderT s ∘ WriterT s = Compose ((->) s) ∘ WriterT s
  fstrength (Day (StateT sgs) h op) = StateT $ \s -> Day (sgs s) h (\(b, s') c -> (op b c, s'))

instance (FStrong ff, FStrong gg) => FStrong (FCompose ff gg) where
  fstrength = FCompose . ffmap fstrength . fstrength . coerce

instance FStrong (EnvT e) where
  fstrength (Day (EnvT e g) h op) = EnvT e (Day g h op)

instance FStrong (TracedT m) where
  fstrength (Day (TracedT gf) h op) = TracedT (Day gf h (\mb c m -> op (mb m) c))

instance FStrong (StoreT s) where
  fstrength (Day (StoreT gf s) h op) = StoreT (Day gf h (\sb c s' -> op (sb s') c)) s