monad-actions-2.0.0.0: src/Control/Monad/Action/Records.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoFieldSelectors #-}
-- | This module should be used with @OverloadedRecordDot@ and/or @RebindableSyntax@ (and @RecordWildCards@).
module Control.Monad.Action.Records where
import Control.Monad qualified as M (Monad (..), join, (=<<))
import Control.Monad.Codensity (Codensity (..))
import Control.Monad.Error.Class (MonadError, liftEither)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.RWS (MonadRWS, RWS, RWST (..), runRWS)
import Control.Monad.Reader (MonadReader (..), Reader, ReaderT (..), runReader)
import Control.Monad.State (MonadState (..), State, StateT (..), runState)
import Control.Monad.Trans.Writer (WriterT (..))
import Control.Monad.TransformerStack
import Control.Monad.Writer (MonadWriter (..), Writer, runWriter)
import Data.Bifunctor (second)
import Data.Constraint (Dict (..))
import Data.Functor.Compose (Compose (..))
import Data.Kind (Constraint, Type)
import Data.List.NonEmpty qualified as NE
import Data.Maybe (maybeToList)
import Prelude hiding ((<*>), (=<<), (>>), (>>=))
import Prelude qualified as P
infixl 1 >>=
infixr 1 =<<
infixl 1 >>
infixr 1 >=>
infixr 1 <=<
infixl 4 <*>
-- | Every @'LeftAction'@ @l@ should satisfy the following laws:
--
-- * @l.'join' '.' 'Control.Monad.join' = l.'join' '.' 'fmap' l.'join'@
--
-- * @l.'join' '.' 'pure' = 'id'@
--
-- All of the operators should match the default implementations in "Control.Monad.Action" and "Control.Monad.Left".
data LeftAction (action :: (Type -> Type) -> (Type -> Type) -> Constraint) where
LeftAction ::
{ -- | left monad action scalar multiplication
join :: forall m f a. (action m f) => m (f a) -> f a,
-- | left monad action bind
(>>=) :: forall m f a b. (action m f) => m a -> (a -> f b) -> f b,
-- | left monad action bind with arguments reversed
(=<<) :: forall m f a b. (action m f) => (a -> f b) -> m a -> f b,
-- | left to right Kleisli arrow scalar multiplication induced by a left monad action
(>=>) :: forall m f a b c. (action m f) => (a -> m b) -> (b -> f c) -> a -> f c,
-- | right to left Kleisli arrow scalar multiplication induced by a left monad action
(<=<) :: forall m f a b c. (action m f) => (b -> f c) -> (a -> m b) -> a -> f c,
-- | left monad action sequencing operator
(>>) :: forall m f a b. (action m f) => m a -> f b -> f b,
-- | scalar sequential application, used for desugaring applicative do blocks
(<*>) :: forall m f a b. (action m f) => m (a -> b) -> f a -> f b
} ->
LeftAction action
-- | Every @'RightAction'@ @r@ should satisfy the following laws:
--
-- * @r.'join' '.' 'fmap' 'Control.Monad.join' = r.'join' '.' r.'join'@
--
-- * @r.'join' '.' 'fmap' 'pure' = 'id'@
--
-- All of the operators should match the default implementations in "Control.Monad.Action" and "Control.Monad.Right".
data RightAction (action :: (Type -> Type) -> (Type -> Type) -> Constraint) where
RightAction ::
{ -- | right monad action scalar multiplication
join :: forall m f a. (action m f) => f (m a) -> f a,
-- | right monad action bind
(>>=) :: forall m f a b. (action m f) => f a -> (a -> m b) -> f b,
-- | right monad action bind with arguments reversed
(=<<) :: forall m f a b. (action m f) => (a -> m b) -> f a -> f b,
-- | left to right Kleisli arrow scalar multiplication induced by a right monad action
(>=>) :: forall m f a b c. (action m f) => (a -> f b) -> (b -> m c) -> a -> f c,
-- | right to left Kleisli arrow scalar multiplication induced by a right monad action
(<=<) :: forall m f a b c. (action m f) => (b -> m c) -> (a -> f b) -> a -> f c,
-- | right monad action sequencing operator
(>>) :: forall m f a b. (action m f) => f a -> m b -> f b,
-- | scalar sequential application, used for desugaring applicative do blocks
(<*>) :: forall m f a b. (action m f) => f (a -> b) -> m a -> f b
} ->
RightAction action
-- | Every @'BiAction'@ @b@ should satisfy the following laws, in addition to the laws for left and right actions:
--
-- * @b.'right'.'join' '.' b.'left'.'join' = b.'left'.'join' '.' 'fmap' b.'right'.'join'@
data BiAction (action :: (Type -> Type) -> (Type -> Type) -> Constraint) where
BiAction ::
{ left :: LeftAction action,
right :: RightAction action
} ->
BiAction action
-- | @'MonadHomomorphism' c@ means that, whenever @c m n@, there is a monad homomorphism @'hom'@ from @m@ to @n@.
class MonadHomomorphism (c :: (Type -> Type) -> (Type -> Type) -> Constraint) where
hom :: forall m n a. (c m n) => m a -> n a
mDict :: forall m n. (c m n) => (Dict (Monad m), Dict (Monad n))
-- | Two-sided action induced by a monad homomorphism.
monadMorphAction :: forall action. (MonadHomomorphism action) => BiAction action
monadMorphAction =
let left =
let join :: forall m n a. (action m n) => m (n a) -> n a
join = case mDict @action @m @n of (_, Dict) -> M.join . hom @action
(>>=) :: forall m n a b. (action m n) => m a -> (a -> n b) -> n b
(>>=) = case mDict @action @m @n of (_, Dict) -> (P.>>=) . hom @action
(=<<) :: forall m n a b. (action m n) => (a -> n b) -> m a -> n b
(=<<) = flip (>>=)
(>=>) :: forall m n a b c. (action m n) => (a -> m b) -> (b -> n c) -> a -> n c
f >=> g = \x -> f x >>= g
(<=<) :: forall m n a b c. (action m n) => (b -> n c) -> (a -> m b) -> a -> n c
(<=<) = flip (>=>)
(>>) :: forall m n a b. (action m n) => m a -> n b -> n b
a >> b = a >>= const b
(<*>) :: forall m n a b. (action m n) => m (a -> b) -> n a -> n b
(<*>) = case mDict @action @m @n of (_, Dict) -> (P.<*>) . hom @action
in LeftAction {..} :: LeftAction action
right =
let join :: forall m n a. (action m n) => n (m a) -> n a
join = case mDict @action @m @n of (_, Dict) -> (hom @action M.=<<)
(>>=) :: forall m n a b. (action m n) => n a -> (a -> m b) -> n b
(>>=) = flip (=<<)
(=<<) :: forall m n a b. (action m n) => (a -> m b) -> n a -> n b
(=<<) = case mDict @action @m @n of (_, Dict) -> (M.=<<) . (hom @action .)
(>=>) :: forall m n a b c. (action m n) => (a -> n b) -> (b -> m c) -> a -> n c
f >=> g = \x -> f x >>= g
(<=<) :: forall m n a b c. (action m n) => (b -> m c) -> (a -> n b) -> a -> n c
(<=<) = flip (>=>)
(>>) :: forall m n a b. (action m n) => n a -> m b -> n b
a >> b = a >>= const b
(<*>) :: forall m n a b. (action m n) => n (a -> b) -> m a -> n b
f <*> x = case mDict @action @m @n of (_, Dict) -> f P.<*> hom @action x
in RightAction {..} :: RightAction action
in BiAction {..}
instance MonadHomomorphism MonadTransStack where
hom = liftStack
mDict = (Dict, Dict)
transformerStackAction :: BiAction MonadTransStack
transformerStackAction = monadMorphAction
-- | @m ':<:' n@ means that @m@ is a submonad of @n@. @'inject'@ must be a monic monad homomorphism.
class (Monad m, Monad n) => m :<: n where
inject :: forall a. m a -> n a
instance MonadHomomorphism (:<:) where
hom = inject
mDict = (Dict, Dict)
submonadAction :: BiAction (:<:)
submonadAction = monadMorphAction
instance (Monad m) => m :<: m where
inject = id
-- | A @'Maybe'@ is just a list of length at most 1.
instance Maybe :<: [] where
inject = maybeToList
-- | A @'Data.List.NonEmpty.NonEmpty'@ is just a list of length at least 1.
instance NE.NonEmpty :<: [] where
inject = NE.toList
-- | @'ReaderT'@ is just read-only @'StateT'@.
instance (m :<: n) => ReaderT s m :<: StateT s n where
inject ReaderT {runReaderT} = StateT $ \s -> inject . fmap (,s) $ runReaderT s
-- | @'WriterT'@ is just append-only @'StateT'@.
instance (Monoid s, m :<: n) => WriterT s m :<: StateT s n where
inject WriterT {runWriterT} = StateT $ \s -> inject @m @n . fmap (second (s <>)) $ runWriterT
-- | @'StateT'@ is just @'RWST'@ that ignores the read-only environment and doesn't append to the output.
instance (Monoid w, m :<: n) => StateT s m :<: RWST r w s n where
inject StateT {runStateT} = RWST $ \_ s -> inject . fmap (\(a, t) -> (a, t, mempty)) $ runStateT s
-- | @'ReaderT'@ is just @'RWST'@ that ignores the state and doesn't append to the output.
--
-- Note: @'inject' \@('ReaderT' s m) \@('StateT' s n) '.' 'inject' \@('StateT' s n) \@('RWST' s w s k) =/= 'inject' \@('ReaderT' s m) \@('RWST' s w s k)@
instance (Monoid w, m :<: n) => ReaderT r m :<: RWST r w s n where
inject ReaderT {runReaderT} = RWST $ \r s -> inject . fmap (,s,mempty) $ runReaderT r
-- | @'WriterT'@ is just @'RWST'@ that ignores the environment and state.
--
-- Note: @'inject' \@('WriterT' w m) \@('StateT' w n) '.' 'inject' \@('StateT' w n) \@('RWST' r w w k) =/= 'inject' \@('WriterT' w m) \@('RWST' r w w k)@
instance (Monoid w, m :<: n) => WriterT w m :<: RWST r w s n where
inject WriterT {runWriterT} = RWST $ \_ s -> inject @m @n . fmap (\(a, w) -> (a, s, w)) $ runWriterT
instance (MonadIO m) => IO :<: m where
inject = liftIO
instance (MonadState s m) => (State s) :<: m where
inject = state . runState
instance (MonadReader r m) => (Reader r) :<: m where
inject = reader . runReader
instance (MonadWriter w m) => (Writer w) :<: m where
inject = writer . runWriter
instance (MonadRWS r w s m) => (RWS r w s) :<: m where
inject t =
ask P.>>= \r ->
get P.>>= \s ->
let (a, s', w) = runRWS t r s
in put s'
M.>> tell w
M.>> pure a
instance (MonadError e m) => (Either e) :<: m where
inject = liftEither
class CodensityAction m f where
codensityJoin :: forall a. m (f a) -> f a
codensityBind :: forall a b. m a -> (a -> f b) -> f b
codensityApply :: forall a b. m (a -> b) -> f a -> f b
codensityAction :: LeftAction CodensityAction
codensityAction =
let join :: forall m f a. (CodensityAction m f) => m (f a) -> f a
join = codensityJoin
(>>=) :: forall m f a b. (CodensityAction m f) => m a -> (a -> f b) -> f b
(>>=) = codensityBind
(=<<) :: forall m f a b. (CodensityAction m f) => (a -> f b) -> m a -> f b
(=<<) = flip codensityBind
(>=>) :: forall m f a b c. (CodensityAction m f) => (a -> m b) -> (b -> f c) -> a -> f c
f >=> g = \x -> f x >>= g
(<=<) :: forall m f a b c. (CodensityAction m f) => (b -> f c) -> (a -> m b) -> a -> f c
(<=<) = flip (>=>)
(>>) :: forall m f a b. (CodensityAction m f) => m a -> f b -> f b
a >> b = a >>= const b
(<*>) :: forall m f a b. (CodensityAction m f) => m (a -> b) -> f a -> f b
(<*>) = codensityApply
in LeftAction {..}
instance (Monad m) => CodensityAction m m where
codensityJoin = M.join
codensityBind = (P.>>=)
codensityApply = (P.<*>)
instance (Functor f) => CodensityAction (Codensity f) f where
codensityJoin c = runCodensity c id
a `codensityBind` f = runCodensity (f P.<$> a) id
fs `codensityApply` xs = fs `codensityBind` flip fmap xs
class (Monad m, Functor f) => LeftCompAction m f where
leftCompJoin :: forall a. m (f a) -> f a
leftCompBind :: forall a b. m a -> (a -> f b) -> f b
leftCompApply :: forall a b. m (a -> b) -> f a -> f b
-- | Left action of any monad @m@ on any composition of functors with @m@ as the outermost component.
leftCompAction :: LeftAction LeftCompAction
leftCompAction =
let join :: forall m f a. (LeftCompAction m f) => m (f a) -> f a
join = leftCompJoin
(>>=) :: forall m f a b. (LeftCompAction m f) => m a -> (a -> f b) -> f b
(>>=) = leftCompBind
(=<<) :: forall m f a b. (LeftCompAction m f) => (a -> f b) -> m a -> f b
(=<<) = flip leftCompBind
(>=>) :: forall m f a b c. (LeftCompAction m f) => (a -> m b) -> (b -> f c) -> a -> f c
f >=> g = \x -> f x >>= g
(<=<) :: forall m f a b c. (LeftCompAction m f) => (b -> f c) -> (a -> m b) -> a -> f c
(<=<) = flip (>=>)
(>>) :: forall m f a b. (LeftCompAction m f) => m a -> f b -> f b
a >> b = a >>= const b
(<*>) :: forall m f a b. (LeftCompAction m f) => m (a -> b) -> f a -> f b
(<*>) = leftCompApply
in LeftAction {..}
instance (Monad m) => LeftCompAction m m where
leftCompJoin = M.join
leftCompBind = (P.>>=)
leftCompApply = (P.<*>)
instance (LeftCompAction m f, Functor g) => LeftCompAction m (Compose f g) where
leftCompJoin = Compose . leftCompJoin . fmap getCompose
a `leftCompBind` f = Compose $ a `leftCompBind` (getCompose . f)
fs `leftCompApply` xs = fs `leftCompBind` flip fmap xs
class (Monad m, Functor f) => RightCompAction m f where
rightCompJoin :: forall a. f (m a) -> f a
rightCompBind :: forall a b. f a -> (a -> m b) -> f b
rightCompApply :: forall a b. f (a -> b) -> m a -> f b
-- | Right action of any monad @m@ on any composition of functors with @m@ as the innermost component.
rightCompAction :: RightAction RightCompAction
rightCompAction =
let join :: forall m f a. (RightCompAction m f) => f (m a) -> f a
join = rightCompJoin
(>>=) :: forall m f a b. (RightCompAction m f) => f a -> (a -> m b) -> f b
(>>=) = rightCompBind
(=<<) :: forall m f a b. (RightCompAction m f) => (a -> m b) -> f a -> f b
(=<<) = flip rightCompBind
(>=>) :: forall m f a b c. (RightCompAction m f) => (a -> f b) -> (b -> m c) -> a -> f c
f >=> g = \x -> f x >>= g
(<=<) :: forall m f a b c. (RightCompAction m f) => (b -> m c) -> (a -> f b) -> a -> f c
(<=<) = flip (>=>)
(>>) :: forall m f a b. (RightCompAction m f) => f a -> m b -> f b
a >> b = a >>= const b
(<*>) :: forall m f a b. (RightCompAction m f) => f (a -> b) -> m a -> f b
(<*>) = rightCompApply
in RightAction {..}
instance (Monad m) => RightCompAction m m where
rightCompJoin = M.join
rightCompBind = (P.>>=)
rightCompApply = (P.<*>)
instance (RightCompAction m f, Functor g) => RightCompAction m (Compose g f) where
rightCompJoin = Compose . fmap rightCompJoin . getCompose
a `rightCompBind` f = Compose . fmap (`rightCompBind` f) $ getCompose a
fs `rightCompApply` xs = fs `rightCompBind` flip fmap xs