{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Control.Algebra.Free
(
FreeAlgebra1 (..)
, Proof (..)
, proof
, AlgebraType0
, AlgebraType
, wrapFree
, foldFree1
, unFoldNatFree
, hoistFree1
, hoistFreeH
, joinFree1
, bindFree1
, assocFree1
, iterFree1
, cataFree1
, DayF (..)
, dayToAp
, apToDay
, MonadList (..)
, MonadMaybe (..)
) where
import Control.Applicative (Alternative)
import Control.Applicative.Free (Ap)
import qualified Control.Applicative.Free as Ap
import qualified Control.Applicative.Free.Fast as Fast
import qualified Control.Applicative.Free.Final as Final
import Control.Alternative.Free (Alt (..))
import qualified Control.Alternative.Free as Alt
import Control.Monad (foldM, join)
import Control.Monad.Except (ExceptT (..), MonadError (..))
import Control.Monad.Free (Free)
import qualified Control.Monad.Free as Free
import qualified Control.Monad.Free.Church as Church
import Control.Monad.List (ListT (..))
import Control.Monad.Reader (MonadReader (..), ReaderT (..))
import Control.Monad.RWS.Class (MonadRWS)
import Control.Monad.RWS.Lazy as L (RWST (..))
import Control.Monad.RWS.Strict as S (RWST (..))
import Control.Monad.State.Class (MonadState (..))
import qualified Control.Monad.State.Lazy as L (StateT (..))
import qualified Control.Monad.State.Strict as S (StateT (..))
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (MaybeT (..))
import Control.Monad.Writer.Class (MonadWriter (..))
import qualified Control.Monad.Writer.Lazy as L (WriterT (..))
import qualified Control.Monad.Writer.Strict as S (WriterT (..))
import Data.Constraint (Dict (..))
import Data.Kind (Type)
import Data.Fix (Fix, cataM)
import Data.Functor.Coyoneda (Coyoneda (..), liftCoyoneda)
import Data.Functor.Day (Day (..))
import qualified Data.Functor.Day as Day
import Data.Functor.Identity (Identity (..))
import Data.Algebra.Free (AlgebraType, AlgebraType0, Proof (..), proof)
class FreeAlgebra1 (m :: (Type -> Type) -> Type -> Type) where
liftFree :: AlgebraType0 m f => f a -> m f a
foldNatFree
:: forall d f a .
( AlgebraType m d
, AlgebraType0 m f
)
=> (forall x. f x -> d x)
-> (m f a -> d a)
codom1 :: forall f. AlgebraType0 m f => Proof (AlgebraType m (m f)) (m f)
forget1 :: forall f. AlgebraType m f => Proof (AlgebraType0 m f) (m f)
wrapFree
:: ( FreeAlgebra1 m
, AlgebraType0 m f
, Monad (m f)
)
=> f (m f a)
-> m f a
wrapFree = join . liftFree
{-# INLINE wrapFree #-}
foldFree1 :: forall m f a .
( FreeAlgebra1 m
, AlgebraType m f
)
=> m f a
-> f a
foldFree1 = case forget1 @m @f of
Proof Dict -> foldNatFree id
{-# INLINE foldFree1 #-}
unFoldNatFree
:: ( FreeAlgebra1 m
, AlgebraType0 m f
)
=> (forall x . m f x -> d x)
-> f a -> d a
unFoldNatFree nat = nat . liftFree
hoistFree1 :: forall m f g a .
( FreeAlgebra1 m
, AlgebraType0 m g
, AlgebraType0 m f
)
=> (forall x. f x -> g x)
-> m f a
-> m g a
hoistFree1 nat = case codom1 @m @g of
Proof Dict -> foldNatFree (liftFree . nat)
{-# INLINE hoistFree1 #-}
hoistFreeH :: forall m n f a .
( FreeAlgebra1 m
, FreeAlgebra1 n
, AlgebraType0 m f
, AlgebraType0 n f
, AlgebraType m (n f)
)
=> m f a
-> n f a
hoistFreeH = foldNatFree liftFree
{-# INLINE hoistFreeH #-}
joinFree1 :: forall m f a .
( FreeAlgebra1 m
, AlgebraType0 m f
)
=> m (m f) a
-> m f a
joinFree1 = case codom1 @m @f of
Proof Dict -> case forget1 @m @(m f) of
Proof Dict -> foldFree1
{-# INLINE joinFree1 #-}
bindFree1 :: forall m f g a .
( FreeAlgebra1 m
, AlgebraType0 m g
, AlgebraType0 m f
)
=> m f a
-> (forall x . f x -> m g x)
-> m g a
bindFree1 mfa nat = case codom1 @m @g of
Proof Dict -> foldNatFree nat mfa
{-# INLINE bindFree1 #-}
assocFree1 :: forall m f a .
( FreeAlgebra1 m
, AlgebraType m f
, Functor (m (m f))
)
=> m f (m f a)
-> m (m f) (f a)
assocFree1 = case forget1 @m @f of
Proof Dict -> case codom1 @m @f of
Proof Dict -> case forget1 @m @(m f) of
Proof Dict -> case codom1 @m @(m f) of
Proof Dict -> fmap foldFree1 <$> foldNatFree (hoistFree1 liftFree . liftFree)
{-# INLINE assocFree1 #-}
cataFree1 :: forall m f a .
( FreeAlgebra1 m
, AlgebraType m f
, Monad f
, Traversable (m f)
)
=> Fix (m f)
-> f a
cataFree1 = cataM foldFree1
iterFree1 :: forall m f a .
( FreeAlgebra1 m
, AlgebraType0 m f
, AlgebraType m Identity
)
=> (forall x . f x -> x)
-> m f a
-> a
iterFree1 f = runIdentity . foldNatFree @_ @Identity (Identity . f)
{-# INLINE iterFree1 #-}
type instance AlgebraType0 Coyoneda g = ()
type instance AlgebraType Coyoneda g = Functor g
instance FreeAlgebra1 Coyoneda where
liftFree = liftCoyoneda
foldNatFree nat (Coyoneda ba fx) = ba <$> nat fx
codom1 = proof
forget1 = proof
type instance AlgebraType0 Ap g = Functor g
type instance AlgebraType Ap g = Applicative g
instance FreeAlgebra1 Ap where
liftFree = Ap.liftAp
foldNatFree = Ap.runAp
codom1 = proof
forget1 = proof
type instance AlgebraType0 Fast.Ap g = Functor g
type instance AlgebraType Fast.Ap g = Applicative g
instance FreeAlgebra1 Fast.Ap where
liftFree = Fast.liftAp
foldNatFree = Fast.runAp
codom1 = proof
forget1 = proof
type instance AlgebraType0 Final.Ap g = Functor g
type instance AlgebraType Final.Ap g = Applicative g
instance FreeAlgebra1 Final.Ap where
liftFree = Final.liftAp
foldNatFree = Final.runAp
codom1 = proof
forget1 = proof
newtype DayF f a = DayF { runDayF :: Day f f a}
deriving (Functor, Applicative)
dayToAp :: Applicative f => Day f f a -> Ap f a
dayToAp = hoistFreeH . DayF
apToDay :: Applicative f => Ap f a -> Day f f a
apToDay = runDayF . hoistFreeH
type instance AlgebraType0 DayF g = Applicative g
type instance AlgebraType DayF g = Applicative g
instance FreeAlgebra1 DayF where
liftFree fa = DayF $ Day fa fa const
foldNatFree nat (DayF day)
= Day.dap . Day.trans2 nat . Day.trans1 nat $ day
codom1 = proof
forget1 = proof
type instance AlgebraType0 Free f = Functor f
type instance AlgebraType Free m = Monad m
instance FreeAlgebra1 Free where
liftFree = Free.liftF
foldNatFree = Free.foldFree
codom1 = proof
forget1 = proof
type instance AlgebraType0 Church.F f = Functor f
type instance AlgebraType Church.F m = Monad m
instance FreeAlgebra1 Church.F where
liftFree = Church.liftF
foldNatFree = Church.foldF
codom1 = proof
forget1 = proof
type instance AlgebraType0 Alt f = Functor f
type instance AlgebraType Alt m = Alternative m
instance FreeAlgebra1 Alt where
liftFree = Alt.liftAlt
foldNatFree = Alt.runAlt
codom1 = proof
forget1 = proof
type instance AlgebraType0 (L.StateT s) m = Monad m
type instance AlgebraType (L.StateT s) m = ( MonadState s m )
instance FreeAlgebra1 (L.StateT s) where
liftFree = lift
foldNatFree nat ma = do
(a, s) <- get >>= nat . L.runStateT ma
put s
return a
codom1 = proof
forget1 = proof
type instance AlgebraType0 (S.StateT s) m = Monad m
type instance AlgebraType (S.StateT s) m = ( MonadState s m )
instance FreeAlgebra1 (S.StateT s) where
liftFree :: Monad m => m a -> S.StateT s m a
liftFree = lift
foldNatFree nat ma = do
(a, s) <- get >>= nat . S.runStateT ma
put s
return a
codom1 = proof
forget1 = proof
type instance AlgebraType0 (L.WriterT w) m = ( Monad m, Monoid w )
type instance AlgebraType (L.WriterT w) m = ( MonadWriter w m )
instance FreeAlgebra1 (L.WriterT w) where
liftFree = lift
foldNatFree nat (L.WriterT m) = fst <$> nat m
codom1 = proof
forget1 = proof
type instance AlgebraType0 (S.WriterT w) m = ( Monad m, Monoid w )
type instance AlgebraType (S.WriterT w) m = ( MonadWriter w m )
instance FreeAlgebra1 (S.WriterT w) where
liftFree = lift
foldNatFree nat (S.WriterT m) = fst <$> nat m
codom1 = proof
forget1 = proof
type instance AlgebraType0 (ReaderT r) m = ( Monad m )
type instance AlgebraType (ReaderT r) m = ( MonadReader r m )
instance FreeAlgebra1 (ReaderT r) where
liftFree = lift
foldNatFree nat (ReaderT g) =
ask >>= nat . g
codom1 = proof
forget1 = proof
type instance AlgebraType0 (ExceptT e) m = ( Monad m )
type instance AlgebraType (ExceptT e) m = ( MonadError e m )
instance FreeAlgebra1 (ExceptT e) where
liftFree = lift
foldNatFree nat (ExceptT m) = do
ea <- nat m
case ea of
Left e -> throwError e
Right a -> return a
codom1 = proof
forget1 = proof
type instance AlgebraType0 (L.RWST r w s) m = ( Monad m, Monoid w )
type instance AlgebraType (L.RWST r w s) m = MonadRWS r w s m
instance FreeAlgebra1 (L.RWST r w s) where
liftFree = lift
foldNatFree nat (L.RWST fn) = do
r <- ask
s <- get
(a, s', w) <- nat $ fn r s
put s'
tell w
return a
codom1 = proof
forget1 = proof
type instance AlgebraType0 (S.RWST r w s) m = ( Monad m, Monoid w )
type instance AlgebraType (S.RWST r w s) m = MonadRWS r w s m
instance FreeAlgebra1 (S.RWST r w s) where
liftFree = lift
foldNatFree nat (S.RWST fn) = do
r <- ask
s <- get
(a, s', w) <- nat $ fn r s
put s'
tell w
return a
codom1 = proof
forget1 = proof
class Monad m => MonadList m where
mempty1 :: m a
mappend1 :: m a -> m a -> m a
mappend1_ :: MonadList m => a -> a -> m a
mappend1_ a b = return a `mappend1` return b
{-# INLINE mappend1_ #-}
instance Monad m => MonadList (ListT m) where
mempty1 = ListT (return [])
mappend1 (ListT ma) (ListT mb) = ListT $ mappend <$> ma <*> mb
type instance AlgebraType0 ListT f = ( Monad f )
type instance AlgebraType ListT m = ( MonadList m )
instance FreeAlgebra1 ListT where
liftFree = lift
foldNatFree nat (ListT mas) = do
as <- nat mas
empty <- mempty1
a <- foldM (\x y -> x `mappend1_` y) empty as
return a
codom1 = proof
forget1 = proof
class MonadMaybe m where
point :: forall a. m a
instance Monad m => MonadMaybe (MaybeT m) where
point = MaybeT (return Nothing)
type instance AlgebraType0 MaybeT m = ( Monad m )
type instance AlgebraType MaybeT m = ( Monad m, MonadMaybe m )
instance FreeAlgebra1 MaybeT where
liftFree = lift
foldNatFree nat (MaybeT mma) =
nat mma >>= \ma -> case ma of
Nothing -> point
Just a -> return a
codom1 = proof
forget1 = proof