free-category-0.0.4.4: src/Control/Category/FreeEffect.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_HADDOCK show-extensions #-}
module Control.Category.FreeEffect
( EffectCategory (..)
, EffCat (..)
, liftEffect
, foldNatEffCat
, runEffCat
, liftKleisli
) where
import Prelude hiding (id, (.))
import Control.Arrow (Kleisli (..))
import Control.Category (Category (..))
import Data.Functor.Identity (Identity (..))
import Data.Kind (Type)
import Control.Algebra.Free2 (FreeAlgebra2 (..))
import Data.Algebra.Free (AlgebraType, AlgebraType0, Proof (..))
-- | Categories which can lift monadic actions, i.e effectful categories.
--
class Category c => EffectCategory c m | c -> m where
effect :: m (c a b) -> c a b
instance Monad m => EffectCategory (Kleisli m) m where
effect m = Kleisli (\a -> m >>= \(Kleisli f) -> f a)
instance EffectCategory (->) Identity where
effect = runIdentity
-- | Category transformer, which adds @'EffectCategory'@ instance to the
-- underlying base category.
--
data EffCat :: (Type -> Type) -> (k -> k -> Type) -> k -> k -> Type where
Base :: c a b -> EffCat m c a b
Effect :: m (EffCat m c a b) -> EffCat m c a b
instance (Functor m, Category c) => Category (EffCat m c) where
id = Base id
Base f . Base g = Base $ f . g
f . Effect mg = Effect $ (f .) <$> mg
Effect mf . g = Effect $ (. g) <$> mf
instance (Functor m, Category c) => EffectCategory (EffCat m c) m where
effect = Effect
type instance AlgebraType0 (EffCat m) c = (Monad m, Category c)
type instance AlgebraType (EffCat m) c = EffectCategory c m
instance Monad m => FreeAlgebra2 (EffCat m) where
liftFree2 = Base
foldNatFree2 nat (Base cab) = nat cab
foldNatFree2 nat (Effect mcab) = effect $ foldNatFree2 nat <$> mcab
codom2 = Proof
forget2 = Proof
-- | Wrap a transition into @'EffCat' cat@ for any free category 'cat' (e.g.
-- 'Cat').
--
liftEffect :: ( Monad m
, FreeAlgebra2 cat
, AlgebraType0 cat tr
, Category (cat tr)
)
=> tr a b -> EffCat m (cat tr) a b
liftEffect = liftFree2 . liftFree2
-- | Fold @'FreeLifting'@ category based on a free category @'cat' tr@ (e.g.
-- @'C' tr@) using a functor @tr x y -> c x y@.
--
foldNatEffCat
:: ( Monad m
, FreeAlgebra2 cat
, AlgebraType cat c
, AlgebraType0 cat tr
, Category (cat tr)
, EffectCategory c m
)
=> (forall x y. tr x y -> c x y)
-> EffCat m (cat tr) a b
-> c a b
foldNatEffCat nat = foldNatFree2 (foldNatFree2 nat)
-- | Join all effects in a free effectful category 'EffCat'.
--
runEffCat
:: Monad m
=> EffCat m c a b
-> m (c a b)
runEffCat (Base f) = return f
runEffCat (Effect mf) = runEffCat =<< mf
-- | Functor from @(->)@ category to @'Kleisli' m@. If @m@ is 'Identity' then
-- it will respect 'effect' i.e.
-- @'liftKleisli' ('effect' ar) = 'effect' ('liftKleisli' \<$\> ar)@.
--
liftKleisli :: Applicative m => (a -> b) -> Kleisli m a b
liftKleisli f = Kleisli (pure . f)