{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Functor.Foldable.Monadic
( cataM, anaM
, paraM, apoM
, histoM, futuM
, histoM', futuM'
, zygoM, cozygoM
, hyloM, metaM
, chronoM, cochronoM
, chronoM',
) where
import Control.Comonad (Comonad (..))
import Control.Comonad.Cofree (Cofree (..))
import qualified Control.Comonad.Trans.Cofree as Cf (CofreeF (..))
import Control.Monad ((<=<), liftM2)
import Control.Monad.Free (Free (..))
import qualified Control.Monad.Trans.Free as Fr (FreeF (..))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT, ask, runReaderT)
import Data.Functor.Foldable (Recursive (..), Corecursive (..), Base, Fix (..))
cataM :: (Monad m, Traversable (Base t), Recursive t)
=> (Base t a -> m a)
-> t -> m a
cataM phi = h
where h = phi <=< mapM h . project
anaM :: (Monad m, Traversable (Base t), Corecursive t)
=> (a -> m (Base t a))
-> a -> m t
anaM psi = h
where h = (return . embed) <=< mapM h <=< psi
paraM :: (Monad m, Traversable (Base t), Recursive t)
=> (Base t (t, a) -> m a)
-> t -> m a
paraM phi = h
where h = phi <=< mapM (liftM2 (,) <$> return <*> h) . project
apoM :: (Monad m, Traversable (Base t), Corecursive t)
=> (a -> m (Base t (Either t a)))
-> a -> m t
apoM psi = h
where h = (return . embed) <=< mapM (either return h) <=< psi
histoM :: (Monad m, Traversable (Base t), Recursive t)
=> (Base t (Cofree (Base t) a) -> m a)
-> t -> m a
histoM phi = h
where h = phi <=< mapM f . project
f = anaM (liftM2 (Cf.:<) <$> h <*> (return . project))
histoM' :: (Monad m, Traversable (Base t), Recursive t)
=> (Base t (Cofree (Base t) a) -> m a)
-> t -> m a
histoM' phi = return . extract <=< cataM f
where f = liftM2 (:<) <$> phi <*> return
futuM :: (Monad m, Traversable (Base t), Corecursive t)
=> (a -> m (Base t (Free (Base t) a)))
-> a -> m t
futuM psi = h
where h = (return . embed) <=< mapM f <=< psi
f = cataM $ \case
Fr.Pure a -> h a
Fr.Free fb -> return (embed fb)
futuM' :: (Monad m, Traversable (Base t), Corecursive t)
=> (a -> m (Base t (Free (Base t) a)))
-> a -> m t
futuM' psi = anaM f . Pure
where f (Pure a) = psi a
f (Free fb) = return fb
zygoM :: (Monad m, Traversable (Base t), Recursive t)
=> (Base t a -> m a)
-> (Base t (a, b) -> m b)
-> t -> m b
zygoM f phi = return . snd <=< cataM g
where g = liftM2 (,) <$> (f <=< return . fmap fst) <*> phi
cozygoM :: (Monad m, Traversable (Base t), Corecursive t)
=> (a -> m (Base t a))
-> (b -> m (Base t (Either a b)))
-> b -> m t
cozygoM f psi = anaM g . Right
where g = either (return . fmap Left <=< f) psi
hyloM :: (Monad m, Traversable t)
=> (t b -> m b)
-> (a -> m (t a))
-> a -> m b
hyloM phi psi = h
where h = phi <=< mapM h <=< psi
hyloM' phi psi = cataM phi <=< anaM psi
metaM :: (Monad m, Traversable (Base t), Recursive s, Corecursive t, Base s ~ Base t)
=> (Base t t -> m t)
-> (s -> m (Base s s))
-> s -> m t
metaM phi psi = h
where h = (return . embed) <=< mapM h . project
metaM' :: (Monad m, Corecursive c, Traversable (Base c), Traversable (Base t), Recursive t)
=> (Base t a -> m a)
-> (a -> m (Base c a))
-> t -> m c
metaM' phi psi = anaM psi <=< cataM phi
chronoM' :: (Monad m, Traversable t)
=> (t (Cofree t b) -> m b)
-> (a -> m (t (Free t a)))
-> a -> m b
chronoM' phi psi = return . extract <=< hyloM f g . Pure
where f = liftM2 (:<) <$> phi <*> return
g (Pure a) = psi a
g (Free fb) = return fb
chronoM phi psi = histoM phi <=< futuM psi
cochronoM :: (Monad m, Corecursive c, Traversable (Base c), Traversable (Base t), Recursive t)
=> (Base t (Cofree (Base t) a) -> m a)
-> (a -> m (Base c (Free (Base c) a)))
-> t -> m c
cochronoM phi psi = futuM psi <=< histoM phi