packages feed

functor-monad-0.1.1.0: src/FFunctor/Adjunction.hs

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE BlockArguments #-}
module FFunctor.Adjunction (Adjunction(..)) where


import Data.Coerce ( coerce, Coercible )
import Data.Kind (Constraint)

import Control.Monad.Trans.Identity
import Data.Functor.Day
import Data.Functor.Day.Curried

import Data.Functor.Kan.Lan
import Data.Functor.Kan.Ran
import Data.Functor.Precompose (Precompose(..))
import Data.Functor.Compose (Compose(..))

import qualified Data.Bifunctor.Sum as Bi
import qualified Data.Bifunctor.Product as Bi
import qualified Data.Bifunctor.Product.Extra as Bi

import qualified Data.Functor.Adjunction as Rank1
import qualified Control.Monad.Trans.Reader as Rank1
import qualified Control.Monad.Trans.Writer as Rank1
import qualified Control.Monad.Trans.State.Lazy as Rank1
import qualified Control.Comonad.Trans.Env as Rank1
import qualified Control.Comonad.Trans.Traced as Rank1
import qualified Control.Comonad.Trans.Store as Rank1

import FFunctor
import FFunctor.FCompose ( FCompose(..) )
import Data.Functor.Exp
import GHC.Generics ((:*:))

-- | An adjunction between \(\mathrm{Hask}^{\mathrm{Hask}}\) and \(\mathrm{Hask}^{\mathrm{Hask}}\).
type Adjunction :: FF -> FF -> Constraint
class (FFunctor ff, FFunctor uu) => Adjunction ff uu | ff -> uu, uu -> ff where
    {-# MINIMAL (unit, counit) | (leftAdjunct, rightAdjunct) #-}
    unit :: forall g. Functor g => g ~> uu (ff g)
    unit = leftAdjunct id
    counit :: forall g. Functor g => ff (uu g) ~> g
    counit = rightAdjunct id

    leftAdjunct :: forall g h. (Functor g, Functor h) => (ff g ~> h) -> (g ~> uu h)
    leftAdjunct ffg_h = ffmap ffg_h . unit
    rightAdjunct :: forall g h. (Functor g, Functor h) => (g ~> uu h) -> (ff g ~> h)
    rightAdjunct g_uuh = counit . ffmap g_uuh

natCoerce :: forall f' f g g'. (Coercible f' f, Coercible g g') => (f ~> g) -> (f' ~> g')
natCoerce fg =
    let f'g' :: forall x. f' x -> g' x
        f'g' = coerce (fg @x)
    in f'g'

instance Adjunction IdentityT IdentityT where
    unit = coerce
    counit = coerce
    leftAdjunct = natCoerce 
    rightAdjunct = natCoerce

instance Functor f => Adjunction (Day f) (Curried f) where
    unit = unapplied
    counit = applied

    leftAdjunct = toCurried
    rightAdjunct = fromCurried

instance Functor f => Adjunction (Lan f) (Precompose f) where
    unit :: (Functor g) => g ~> Precompose f (Lan f g)
    unit g = Precompose $ Lan id g
    counit :: (Functor g) => Lan f (Precompose f g) ~> g
    counit (Lan unF (Precompose gf)) = fmap unF gf

instance Functor f => Adjunction (Precompose f) (Ran f) where
    unit :: (Functor g) => g ~> Ran f (Precompose f g)
    -- g ~> Ran f (g :.: f)
    -- ∀x. g x -> (∀y. (x -> f y) -> g (f y))
    unit g = Ran $ \toF -> Precompose (fmap toF g)

    counit :: (Functor g) => Precompose f (Ran f g) ~> g
    -- Ran f g :.: f ~> g
    -- ∀x. (∀y. (f x -> f y) -> g y) -> g x
    counit (Precompose ffg) = runRan ffg id

instance (Adjunction ff uu, Adjunction gg vv) => Adjunction (FCompose ff gg) (FCompose vv uu) where
    unit :: Functor h => h ~> FCompose vv uu (FCompose ff gg h)
    unit = ffmap FCompose . FCompose . ffmap unit . unit

    counit :: Functor h => FCompose ff gg (FCompose vv uu h) ~> h
    counit = counit . ffmap counit . getFCompose . ffmap getFCompose

    leftAdjunct :: forall h k. (Functor h, Functor k)
      => (FCompose ff gg h ~> k) -> h ~> FCompose vv uu k
    leftAdjunct lefty = FCompose . leftAdjunct (leftAdjunct (lefty . FCompose))

    rightAdjunct :: (Functor h, Functor k)
      => (h ~> FCompose vv uu k) -> FCompose ff gg h ~> k
    rightAdjunct righty = rightAdjunct (rightAdjunct (getFCompose . righty)) . getFCompose

instance (Adjunction ff uu, Adjunction gg vv) => Adjunction (Bi.Sum ff gg) (Bi.Product uu vv) where
    unit :: (Functor h)
      => h ~> Bi.Product uu vv (Bi.Sum ff gg h)
    unit h = Bi.Pair (ffmap Bi.L2 (unit h)) (ffmap Bi.R2 (unit h))

    counit :: (Functor h)
      => Bi.Sum ff gg (Bi.Product uu vv h) ~> h
    counit (Bi.L2 ffP) = counit (ffmap Bi.proj1 ffP)
    counit (Bi.R2 ggP) = counit (ffmap Bi.proj2 ggP)

    leftAdjunct :: (Functor h, Functor k) => (Bi.Sum ff gg h ~> k) -> h ~> Bi.Product uu vv k
    leftAdjunct lefty h = Bi.Pair (leftAdjunct (lefty . Bi.L2) h) (leftAdjunct (lefty . Bi.R2) h)

    rightAdjunct :: (Functor h, Functor k) => (h ~> Bi.Product uu vv k) -> Bi.Sum ff gg h ~> k
    rightAdjunct righty (Bi.L2 ff) = rightAdjunct (Bi.proj1 . righty) ff
    rightAdjunct righty (Bi.R2 gg) = rightAdjunct (Bi.proj2 . righty) gg

instance (Rank1.Adjunction f u) => Adjunction (Compose f) (Compose u) where
    unit :: (Functor g) => g ~> Compose u (Compose f g)
    unit gx = Compose . fmap Compose $ Rank1.unit gx
    counit :: (Functor g) => Compose f (Compose u g) ~> g
    counit = Rank1.counit . fmap getCompose . getCompose

instance Adjunction (Rank1.EnvT e) (Rank1.ReaderT e) where
    unit gx = Rank1.ReaderT \e -> Rank1.EnvT e gx
    counit (Rank1.EnvT e (Rank1.ReaderT f)) = f e

instance Adjunction (Rank1.TracedT m) (Rank1.WriterT m) where
    unit gx = Rank1.WriterT $ Rank1.TracedT $ fmap (,) gx

    counit (Rank1.TracedT (Rank1.WriterT gwx)) = fmap (\(f, m) -> f m) gwx

instance Adjunction (Rank1.StoreT s) (Rank1.StateT s) where
    unit gx = Rank1.StateT $ Rank1.StoreT (fmap (,) gx)
    counit (Rank1.StoreT (Rank1.StateT state) s0) = fmap (\(f, m) -> f m) (state s0)

instance Functor f => Adjunction ((:*:) f) (Exp1 f) where 
  leftAdjunct :: (Functor f, Functor g, Functor h) => ((f :*: g) ~> h) -> g ~> Exp1 f h
  leftAdjunct = toExp1
  
  rightAdjunct :: (Functor f, Functor g, Functor h) => (g ~> Exp1 f h) -> (f :*: g) ~> h
  rightAdjunct = fromExp1