supply-chain-core-0.0.0.0: supply-chain-core/SupplyChain/Core/FreeMonad.hs
-- | Description: makes a monad of any type constructor
module SupplyChain.Core.FreeMonad
(
{- * Type -} FreeMonad (Step, Bind, Pure, Map),
{- * Running -} run, eval,
{- * Alteration -} alter,
)
where
import Control.Applicative (Applicative (pure, (<*>)))
import Control.Monad (Monad ((>>=)))
import Data.Function ((&), ($), (.))
import Data.Functor (Functor, (<&>))
import SupplyChain.Core.FreePointedFunctor (FreePointedFunctor)
import qualified Control.Monad as Monad
import qualified SupplyChain.Core.FreePointedFunctor as FreePointedFunctor
data FreeMonad f a =
Step (FreePointedFunctor f a)
| forall x. Bind (FreeMonad f x) (x -> FreeMonad f a)
pattern Pure :: a -> FreeMonad f a
pattern Pure a = Step (FreePointedFunctor.Pure a)
pattern Map :: f x -> (x -> a) -> FreeMonad f a
pattern Map action extract = Step (FreePointedFunctor.Map action extract)
{-# complete Pure, Map, Bind #-}
deriving instance Functor (FreeMonad f)
instance Applicative (FreeMonad f) where pure = Pure; (<*>) = Monad.ap
instance Monad (FreeMonad f) where (>>=) = Bind
run :: Monad effect =>
(forall x. f x -> effect x) -- ^ How to interpret @f@ actions
-> FreeMonad f a -> effect a
run (runEffect :: forall x. f x -> effect x) = recur
where
runPF :: FreePointedFunctor f x -> effect x
runPF = FreePointedFunctor.run runEffect
recur :: FreeMonad f x -> effect x
recur = \case
Step a -> runPF a
Bind (Step a) b -> runPF a >>= \x -> recur $ b x
Bind (Bind a b) c -> recur a >>= \x -> recur $ Bind (b x) c
eval :: (forall x. f x -> x) -- ^ How to interpret @f@ actions
-> FreeMonad f a
-> a
eval (evalEffect :: forall x. f x -> x) = recur
where
evalPF :: FreePointedFunctor f x -> x
evalPF = FreePointedFunctor.eval evalEffect
recur :: FreeMonad f x -> x
recur = \case
Step a -> evalPF a
Bind (Step a) b -> evalPF a & \x -> recur $ b x
Bind (Bind a b) c -> recur a & \x -> recur $ Bind (b x) c
alter :: (forall x. f x -> FreeMonad f' x)
-> FreeMonad f a -> FreeMonad f' a
alter (f :: forall x. f x -> FreeMonad f' x) = recur
where
recur :: FreeMonad f x -> FreeMonad f' x
recur = \case
Pure x -> Pure x
Map action extract -> f action <&> extract
Bind a b -> Bind (recur a) (recur . b)