packages feed

functor-monad-0.1.1.0: src/FMonad.hs

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DerivingVia #-}

-- | Monads in the cateogory of @Functor@s.
module FMonad
  ( type (~>),
    -- * FMonad

    FMonad (..),
    fjoin,
    
    -- * FMonad laws

    -- ** Laws
    --
    -- $fmonad_laws_in_fbind
    
    -- ** Laws (in terms of @fjoin@)
    --
    -- $fmonad_laws_in_fjoin
    
    
    -- * Re-export
    FFunctor (..)
  )
where

import Control.Comonad (Comonad (..), (=>=))
import Control.Monad (join)

import qualified Control.Applicative.Free as FreeAp
import qualified Control.Applicative.Free.Final as FreeApFinal
import Control.Applicative.Lift
import qualified Control.Applicative.Trans.FreeAp as FreeApT
import qualified Control.Monad.Free as FreeM
import qualified Control.Monad.Free.Church as FreeMChurch
import Control.Monad.Trans.Free (FreeT)
import Control.Monad.Trans.Free.Extra ( inr, fbindFreeT_ )

import Control.Monad.Trans.Identity
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Control.Monad.Trans.Writer
import Data.Functor.Compose
import Data.Functor.Day
import Data.Functor.Day.Comonoid hiding (Comonad(..))
import Data.Functor.Day.Curried
import Data.Functor.Day.Extra (uncurried)
import Data.Functor.Flip1
import Data.Functor.Kan.Lan
import Data.Functor.Kan.Ran
import Data.Functor.Product
import Data.Functor.Sum
import FFunctor

import qualified Data.Bifunctor.Product as Bi
import qualified Data.Bifunctor.Product.Extra as Bi
import GHC.Generics
import Data.Kind (Type)


{- $fmonad_laws_in_fbind

Like 'Monad', there is a set of laws which every instance of 'FMonad' should satisfy.

[fpure is natural in g]

   Let @g, h@ be arbitrary @Functor@s. For any natural transformation @n :: g ~> h@,

   > ffmap n . fpure = fpure . n

[fbind is natural in g,h]

   Let @g, g', h, h'@ be arbitrary @Functor@s. For all natural transformations
   @k :: g ~> ff h@, @nat_g :: g' ~> g@, and @nat_h :: h ~> h'@, the following holds.

   > fbind (ffmap nat_h . k . nat_g) = ffmap nat_h . fbind k . ffmap nat_g

[Left unit]

   > fbind k . fpure = k

[Right unit]

   > fbind fpure = id

[Associativity]

   > fbind k . fbind j = fbind (fbind k . j)
-}

{- $fmonad_laws_in_fjoin

Alternatively, 'FMonad' laws can be stated using 'fjoin' instead. 

[fpure is natural in g]

   For all @Functor g@, @Functor h@, and @n :: g ~> h@,

   > ffmap n . fpure = fpure . n

[fjoin is natural in g]

   For all @Functor g@, @Functor h@, and @n :: g ~> h@,

   > ffmap n . fjoin = fjoin . ffmap (ffmap n)

[Left unit]

   > fjoin . fpure = id

[Right unit]

   > fjoin . ffmap fpure = id

[Associativity]

   > fjoin . fjoin = fjoin . ffmap fjoin

-}

{- | @FMonad@ is to 'FFunctor' what 'Monad' is to 'Functor'.


+----------------+-----------------------------+------------------------------------+
|                | @'Monad' m@                 | @'FMonad'   mm@                    |
+================+=============================+====================================+
| Superclass     | @'Functor' m@               | @'FFunctor' mm@                    |
+----------------+-----------------------------+------------------------------------+
| Features       | @                           | @                                  |
|                | return = pure               | fpure                              |
|                |   :: a -> m a               |    :: (Functor g)                  |
|                | @                           |    => g ~> mm g                    |
|                |                             | @                                  |
+----------------+-----------------------------+------------------------------------+
|                | @                           | @                                  |
|                | (=<<)                       | fbind                              |
|                |   :: (a -> m b)             |   :: (Functor g, Functor h)        |
|                |   -> (m a -> m b)           |   => (g ~> mm h)                   |
|                | @                           |   -> (mm g ~> mm h)                |
|                |                             | @                                  |
+----------------+-----------------------------+------------------------------------+



-} 
class FFunctor ff => FMonad ff where
  fpure :: (Functor g) => g ~> ff g
  fbind :: (Functor g, Functor h) => (g ~> ff h) -> ff g a -> ff h a

-- | 'join' but for 'FMonad' instead of 'Monad'.
fjoin :: (FMonad ff, Functor g) => ff (ff g) ~> ff g
fjoin = fbind id

instance Functor f => FMonad (Sum f) where
  fpure = InR

  fbind _ (InL fa) = InL fa
  fbind k (InR ga) = k ga

instance (Functor f, forall a. Monoid (f a)) => FMonad (Product f) where
  fpure = Pair mempty
  fbind k (Pair fa1 ga) = case k ga of
    (Pair fa2 ha) -> Pair (fa1 <> fa2) ha

instance Monad f => FMonad (Compose f) where
  fpure = Compose . return
  fbind k = Compose . (>>= (getCompose . k)) . getCompose

instance Functor f => FMonad ((:+:) f) where
  fpure = R1
  fbind k ff = case ff of
    L1 fx -> L1 fx
    R1 gx -> k gx

instance (Functor f, forall a. Monoid (f a)) => FMonad ((:*:) f) where
  fpure = (mempty :*:)
  fbind k (fa :*: ga) = case k ga of
    fa' :*: ha -> (fa <> fa') :*: ha

deriving
  via (Compose (f :: Type -> Type))
  instance Monad f => FMonad ((:.:) f)

deriving
  via IdentityT
  instance FMonad (M1 c m) 

deriving
  via IdentityT
  instance FMonad Rec1

instance FMonad Lift where
  fpure = Other
  fbind _ (Pure a)   = Pure a
  fbind k (Other ga) = k ga

instance FMonad FreeM.Free where
  fpure = FreeM.liftF
  fbind = FreeM.foldFree

instance FMonad FreeMChurch.F where
  fpure = FreeMChurch.liftF
  fbind = FreeMChurch.foldF

instance FMonad FreeAp.Ap where
  fpure = FreeAp.liftAp
  fbind = FreeAp.runAp

instance FMonad FreeApFinal.Ap where
  fpure = FreeApFinal.liftAp
  fbind = FreeApFinal.runAp

instance FMonad IdentityT where
  fpure = IdentityT
  fbind k = k . runIdentityT

instance FMonad (ReaderT e) where
  -- See the similarity between 'Compose' @((->) e)@

  -- return :: x -> (e -> x)
  fpure = ReaderT . return

  -- join :: (e -> e -> x) -> (e -> x)
  fbind k = ReaderT . (>>= runReaderT . k) . runReaderT

instance Monoid m => FMonad (WriterT m) where
  -- See the similarity between 'FlipCompose' @(Writer m)@

  -- fmap return :: f x -> f (Writer m x)
  fpure = WriterT . fmap (,mempty)

  -- fmap join :: f (Writer m (Writer m x)) -> f (Writer m x)
  fbind k = WriterT . fmap (\((x, m1), m2) -> (x, m2 <> m1)) . runWriterT . runWriterT . ffmap k

{-

If everything is unwrapped, FMonad @(StateT s)@ is

  fpure :: forall f. Functor f => f x -> s -> f (x, s)
  fjoin :: forall f. Functor f => (s -> s -> f ((x, s), s)) -> s -> f (x, s)

And if this type was generic in @s@ without any constraint like @Monoid s@,
the only possible implementations are

  -- fpure is uniquely:
  fpure fx s = (,s) <$> fx

  -- fjoin is one of the following three candidates
  fjoin1 stst s = (\((x,_),_) -> (x,s)) <$> stst s s
  fjoin2 stst s = (\((x,_),s) -> (x,s)) <$> stst s s
  fjoin3 stst s = (\((x,s),_) -> (x,s)) <$> stst s s

But none of them satisfy the FMonad law.

  (fjoin1 . fpure) st
    = fjoin1 $ \s1 s2 -> (,s1) <$> st s2
    = \s -> (\((x,_),_) -> (x,s)) <$> ((,s) <$> st s)
    = \s -> (\(x,_) -> (x,s)) <$> st s
    /= st
  (fjoin2 . fpure) st
    = fjoin2 $ \s1 s2 -> (,s1) <$> st s2
    = \s -> (\((x,_),s') -> (x,s')) <$> ((,s) <$> st s)
    = \s -> (\(x,_) -> (x,s)) <$> st s
    /= st
  (fjoin3 . ffmap fpure) st
    = fjoin2 $ \s1 s2 -> fmap (fmap (,s2)) . st s1
    = \s -> ((\((x,s'),_) -> (x,s')) . fmap (,s)) <$> st s
    = \s -> (\(x,_) -> (x,s)) <$> st s
    /= st

So the lawful @FMonad (StateT s)@ will utilize some structure
on @s@.

One way would be seeing StateT as the composision of Reader s and
Writer s:

  StateT s m ~ Reader s ∘ m ∘ Writer s
    where (∘) = Compose

By this way

  StateT s (StateT s m) ~ Reader s ∘ Reader s ∘ m ∘ Writer s ∘ Writer s

And you can collapse the nesting by applying @join@ for @Reader s ∘ Reader s@
and @Writer s ∘ Writer s@. To do so, it will need @Monoid s@ for @Monad (Writer s)@.

-}

instance Monoid s => FMonad (StateT s) where
  -- Note that this is different to @lift@ in 'MonadTrans',
  -- whilst having similar type and actually equal in
  -- several other 'FMonad' instances.
  --
  -- See the discussion below.
  fpure fa = StateT $ \_ -> (,mempty) <$> fa

  fbind k = StateT . fjoin_ . fmap runStateT . runStateT . ffmap k
    where
      fjoin_ :: forall f a. (Functor f) => (s -> s -> f ((a, s), s)) -> s -> f (a, s)
      fjoin_ = fmap (fmap joinWriter) . joinReader
        where
          joinReader :: forall x. (s -> s -> x) -> s -> x
          joinReader = join

          joinWriter :: forall x. ((x, s), s) -> (x, s)
          joinWriter ((a, s1), s2) = (a, s2 <> s1)

{-

Note [About FMonad (StateT s) instance]

@fpure@ has a similar (Functor instead of Monad) type signature
with 'lift', but due to the different laws expected on them,
they aren't necessarily same.

@lift@ for @StateT s@ must be, by the 'MonadTrans' laws,
the one currently used. And this is not because the parameter @s@
is generic, so it applies if we have @Monoid s =>@ constraints like
the above instance.

One way to have @lift = fpure@ here is requiring @s@ to be a type with
group operations, @Monoid@ + @inv@ for inverse operator,
instead of just being a monoid.

> fpure fa = StateT $ \s -> (,s) <$> fa
> fjoin = StateT . fjoin_ . fmap runStateT . runStateT
>   where fjoin_ mma s = fmap (fmap (joinGroup s)) $ joinReader mma s
>         joinReader = join
>         joinGroup s ((x,s1),s2) = (x, s2 <> inv s <> s1)

-}

-- | @Ran w (Ran w f) ~ Ran ('Compose' w w) f@
instance (Comonad w) => FMonad (Ran w) where
  fpure ::
    forall f x.
    (Functor f) =>
    f x ->
    Ran w f x
  --       f x -> (forall b. (x -> w b) -> f b)
  fpure f = Ran $ \k -> fmap (extract . k) f

  fbind :: (Functor g, Functor h) =>
     (g ~> Ran w h) -> (Ran w g ~> Ran w h)
  fbind k wg = Ran $ \xd -> runRan (k (runRan wg (duplicate . xd))) id

-- | @Lan w (Lan w f) ~ Lan ('Compose' w w) f@
instance (Comonad w) => FMonad (Lan w) where
  fpure ::
    forall f x.
    (Functor f) =>
    f x ->
    Lan w f x
  --       f x -> exists b. (w b -> x, f b)
  fpure f = Lan extract f
  
  fbind :: (Functor g, Functor h) =>
    (g ~> Lan w h) -> (Lan w g ~> Lan w h)
  fbind k (Lan j1 g) = case k g of
    Lan j2 h -> Lan (j2 =>= j1) h

instance (Applicative f) => FMonad (Day f) where
  fpure :: g ~> Day f g
  fpure = day (pure id)

  {-
     day :: f (a -> b) -> g a -> Day f g b
  -}
  
  fbind k = trans1 dap . assoc . trans2 k

{-
   trans2 k   :: Day f g ~> Day f (Day f h)
   assoc      ::            Day f (Day f h) ~> Day (Day f f) h
   trans1 dap ::                               Day (Day f f) h ~> Day f h
-}

instance Comonoid f => FMonad (Curried f) where
  fpure :: Functor g => g a -> Curried f g a
  fpure g = Curried $ \f -> extract f <$> g

  fbind k m = Curried $ \f -> runCurried (uncurried (ffmap k m)) (coapply f)

instance FMonad (FreeApT.ApT f) where
  fpure = FreeApT.liftT
  fbind k = FreeApT.fjoinApTLeft . ffmap k

instance Applicative g => FMonad (Flip1 FreeApT.ApT g) where
  fpure = Flip1 . FreeApT.liftF
  fbind k = Flip1 . FreeApT.foldApT (unFlip1 . k) FreeApT.liftT . unFlip1

instance Functor f => FMonad (FreeT f) where
  fpure = inr
  fbind = fbindFreeT_

instance (FMonad ff, FMonad gg) => FMonad (Bi.Product ff gg) where
  fpure h = Bi.Pair (fpure h) (fpure h)
  fbind k (Bi.Pair ff gg) = Bi.Pair (fbind (Bi.proj1 . k) ff) (fbind (Bi.proj2 . k) gg)