packages feed

monadology-0.1: src/Control/Monad/Ology/Data/Prod.hs

module Control.Monad.Ology.Data.Prod where

import Control.Monad.Ology.General
import Control.Monad.Ology.Specific.WriterT
import Import

-- | A product of a monad (as in 'WriterT').
data Prod m a = MkProd
    { prodTell :: a -> m ()
    , prodListen :: forall r. m r -> m (r, a)
    }

instance Functor m => Invariant (Prod m) where
    invmap f g (MkProd t l) = MkProd (t . g) (\mr -> fmap (fmap f) $ l mr)

instance Applicative m => Productable (Prod m) where
    rUnit = MkProd (\() -> pure ()) $ fmap $ \r -> (r, ())
    (<***>) :: forall a b. Prod m a -> Prod m b -> Prod m (a, b)
    MkProd tellA listenA <***> MkProd tellB listenB = let
        tellAB :: (a, b) -> m ()
        tellAB (a, b) = tellA a *> tellB b
        listenAB :: m r -> m (r, (a, b))
        listenAB m = fmap (\((r, a), b) -> (r, (a, b))) $ listenB (listenA m)
        in MkProd tellAB listenAB

prodListen_ :: Functor m => Prod m a -> m () -> m a
prodListen_ p mu = fmap snd $ prodListen p mu

liftProd :: (MonadTransTunnel t, Monad m) => Prod m --> Prod (t m)
liftProd (MkProd t l) =
    MkProd (\a -> lift $ t a) $ \tmr -> tunnel $ \unlift -> fmap (\(tun, a) -> fmap (\r -> (r, a)) tun) $ l $ unlift tmr

writerProd :: Monad m => Prod (WriterT w m) w
writerProd = MkProd {prodTell = tell, prodListen = listen}

foldProd ::
       forall f m a. (Applicative f, Foldable f, Applicative m)
    => Prod m a
    -> Prod m (f a)
foldProd (MkProd prodTell prodListen) = let
    prodTell' :: f a -> m ()
    prodTell' aa = for_ aa prodTell
    prodListen' :: forall r. m r -> m (r, f a)
    prodListen' mr = fmap (\(r, a) -> (r, pure a)) $ prodListen mr
    in MkProd prodTell' prodListen'