packages feed

functor-monad-0.1.1.1: examples/TrailAsAdjunction.hs

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE TupleSections #-}

-- | Explain 'Control.Monad.Trail.Trail' monad in terms of adjoint functors
module TrailAsAdjunction(
  -- * Building blocks

  Const(..), Pair, Shape(..), Emptied(..), Y, Γ(..),

  mapPair, mapShape, mapEmptied, mapY, mapΓ,

  -- * Adjunctions
  -- $adjunction_diagrams

  adjunctPairShape,
  adjunctPairShape',
  adjunctShapeConst,
  adjunctShapeConst',
  adjunctConstEmptied,
  adjunctConstEmptied',
  adjunctΓY,
  adjunctΓY',

  -- * Monads and Comonads from these adjunctions

  J0(..), K1(..), J1(..), RR(..),

  -- * Examples
  --
  -- $doc_example

  R(..),

) where

import Data.Void
import Data.Bifunctor (Bifunctor(..))
import Data.Functor.Const
import Control.Comonad
import Control.Monad (ap)
import Data.Coerce (coerce)

import FFunctor (FFunctor(..), type (~>))
import FMonad (FMonad (..))
import FComonad (FComonad (..))


{- $adjunction_diagrams

There is a chain of three adjunctions between @Type@ and @Functor@

>                    Type
>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>      |            ^          |            ^
>      |            |          |            |
> Pair |  -|  Shape | -| Const | -| Emptied | 
>      |            |          |            |
>      v            |          v            |
>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>                   Functor

And an adjunction between @Type^op@ and @Functor@

>       Type^op
>   ~~~~~~~~~~~~~~~~
>      ^      |
>      |      |
>    Γ |  -|  | Y
>      |      |
>      |      v
>   ~~~~~~~~~~~~~~~~
>      Functor

-}

{- $doc_example

'J0' and 'J1' turn an @FMonad mm@ to a @Monad@. The following table shows what is the
@Monad@ they make for each @FMonad@.

+-----------+-------------+--------------------+-------------------+------------+------------+
| @mm@      | @Compose m@ | @Precompose m@     | @Sum f@           | @Free@     | @FreeT' m@ |
+===========+=============+====================+===================+============+============+
| @'J0' mm@ | @m@         | @Identity@         | @Either (f Void)@ | @Identity@ | @m@        |
+-----------+-------------+--------------------+-------------------+------------+------------+
| @'J1' mm@ | @m@         | @Writer (Ap m ())@ | @Either (f ())@   | @[]@       | @ListT m@  |
+-----------+-------------+--------------------+-------------------+------------+------------+

'K1' takes an @FComonad ww@ and turns them a @Comonad@. 'RR' takes an @FComonad ww@ and turns
them a @Monad@, instead of @Comonad@.

+-----------+-------------+--------------------+-------------------+------------+---------------------+
| @ww@      | @Compose w@ | @Precompose w@     | @Product f@       | @Cofree@   | @Sum ff gg@         |
+===========+=============+====================+===================+============+=====================+
| @'K1' ww@ | @w@         | @Identity@         | @Env (f ())@      | @Identity@ | @K1 ff :+: K1 gg@   |
+-----------+-------------+--------------------+-------------------+------------+---------------------+
| @'RR' ww@ | @Co w@      | @R w@              | @Exp1 f Identity@ | @[]@       | ??                  |
+-----------+-------------+--------------------+-------------------+------------+---------------------+

Where

- 'Control.Monad.Co.Co' is a @Monad@ defined in "Control.Monad.Co" from kan-extensions package
- 'R' is a @Monad@ defined below
- 'Data.Functor.Exp.Exp1' is defined in "Data.Functor.Exp"

-}

-- * Building blocks

-- | @Pair :: Type -> Functor@
type Pair = (,)

mapPair :: (a -> b) -> (Pair a ~> Pair b)
mapPair = first

-- | Yoneda embedding
--
-- @Y :: Type^op -> Functor@
type Y = (->)

mapY :: (a -> b) -> (Y b ~> Y a)
mapY = flip (.)

-- | @Emptied :: Functor -> Type@
newtype Emptied f = Emptied { getEmptied :: f Void }

mapEmptied :: (f ~> g) -> Emptied f -> Emptied g
mapEmptied fg (Emptied f0) = Emptied (fg f0)

-- | @Shape :: Functor -> Type@
newtype Shape f = Shape { getShape :: f () }

mapShape :: (f ~> g) -> Shape f -> Shape g
mapShape fg (Shape f1) = Shape (fg f1)

-- | @Γ :: Functor -> Type^op@
newtype Γ f = Γ { getΓ :: forall x. f x -> x }

mapΓ :: (f ~> g) -> Γ g -> Γ f
mapΓ fg (Γ k) = Γ (k . fg)

-- * Adjunctions

adjunctPairShape :: (Pair a ~> f) -> (a -> Shape f)
adjunctPairShape h a = Shape (h (a,()))

adjunctPairShape' :: Functor f => (a -> Shape f) -> (Pair a ~> f)
adjunctPairShape' g (a,b) = b <$ getShape (g a)

adjunctShapeConst :: Functor f => (Shape f -> a) -> (f ~> Const a)
adjunctShapeConst h fb = Const $ h (Shape (() <$ fb))

adjunctShapeConst' :: (f ~> Const a) -> (Shape f -> a)
adjunctShapeConst' g (Shape f1) = getConst $ g f1

adjunctConstEmptied :: (Const a ~> f) -> a -> Emptied f
adjunctConstEmptied h a = Emptied (h (Const a))

adjunctConstEmptied' :: Functor f => (a -> Emptied f) -> (Const a ~> f)
adjunctConstEmptied' g (Const a) = vacuous $ getEmptied (g a)


-- | @adjunctΓY :: (Γ f `'Control.Category.Op'` a) -> (f ~> Y a)@
adjunctΓY :: (a -> Γ f) -> (f ~> Y a)
adjunctΓY h fb a = getΓ (h a) fb

-- | @adjunctΓY' :: (f ~> Y a) -> (Γ f `Control.Category.Op` a)@ 
adjunctΓY' :: (f ~> Y a) -> (a -> Γ f)
adjunctΓY' g a = Γ (\fb -> g fb a)

-- * Monads and Comonads from these adjunctions

-- | @J0 mm ~ Emptied ∘ mm ∘ Const@
newtype J0 mm a = J0 { runJ0 :: mm (Const a) Void }

instance FFunctor mm => Functor (J0 mm) where
  fmap f = J0 . ffmap (first f) . runJ0

instance FMonad mm => Applicative (J0 mm) where
  pure = J0 . fpure . Const
  (<*>) = ap

instance FMonad mm => Monad (J0 mm) where
  ma >>= k = J0 $ fbind (adjunctConstEmptied' (coerce . k)) (runJ0 ma)

-- | @K ww ~ Shape ∘ ww ∘ Const@ 
newtype K1 ww a = K1 { runK1 :: ww (Const a) () }

instance FFunctor ww => Functor (K1 ww) where
  fmap f = K1 . ffmap (first f) . runK1

instance FComonad ww => Comonad (K1 ww) where
  extract :: K1 ww a -> a
  extract = getConst . fextract . runK1

  extend :: (K1 ww a -> b) -> K1 ww a -> K1 ww b
  extend h = K1 . fextend (adjunctShapeConst (h . coerce)). runK1

-- | @J1 mm ~ Emptied ∘ mm ∘ Const@
--
-- Also, @J1 ~ 'Control.Monad.Trail.Trail'@
newtype J1 mm a = J1 { runJ1 :: mm (Pair a) () }

instance FFunctor mm => Functor (J1 mm) where
  fmap f = J1 . ffmap (first f) . runJ1

instance FMonad mm => Applicative (J1 mm) where
  pure = J1 . fpure . (, ())
  (<*>) = ap

instance FMonad mm => Monad (J1 mm) where
  ma >>= k = J1 $ fbind (adjunctPairShape' (coerce . k)) (runJ1 ma)

-- | @RR ww ~ Γ ∘ ww ∘ Y@
newtype RR ww a = RR { runRR :: Γ (ww (Y a)) }

instance FFunctor ww => Functor (RR ww) where
  fmap f = RR . mapΓ (ffmap (mapY f)) . runRR

instance FComonad ww => Applicative (RR ww) where
  pure a = RR $ Γ $ \wwk -> fextract wwk a
  (<*>) = ap

instance FComonad ww => Monad (RR ww) where
  ma >>= k = RR $ Γ $ \wwk -> getΓ (runRR ma) (fextend (adjunctΓY (runRR . k)) wwk)

-- | @RR ww@ specialized to @ww = Precompose w@
newtype R w a = R { runR :: forall r. (a -> w r) -> r }

instance Functor (R w) where
  fmap f rw = R $ \cont -> runR rw (cont . f)

instance Comonad w => Applicative (R w) where
  pure a = R $ \cont -> extract (cont a)
  (<*>) = ap

instance Comonad w => Monad (R w) where
  rw >>= k = R $ \cont -> runR rw (\a -> runR (k a) (duplicate . cont))