packages feed

yield-0.0.0.0: library/Control/Monad/Trans/Yield.hs

{-# LANGUAGE UndecidableInstances #-}

module Control.Monad.Trans.Yield
  ( YieldT
  , runYieldT
  , hoistYieldT
  ) where

import Prelude

import Control.Monad (ap, liftM)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Reader (MonadReader (..))
import Control.Monad.State (MonadState (..))
import Control.Monad.Trans.Class (MonadTrans (..))
import Control.Monad.Trans.Codensity (CodensityT (..))
import Control.Monad.Yield.Aggregation (Aggregation (..))
import Control.Monad.Yield.Class (MonadYield (..))

-- | Monad transformer that adds dynamically interpretable 'MonadYield' support
newtype YieldT a m r = YieldT (CodensityT (Yielder a m) r)
  deriving newtype (Functor, Applicative, Monad)

instance MonadTrans (YieldT a) where
  lift = YieldT . lift . lift

deriving newtype instance MonadIO m => MonadIO (YieldT a m)

deriving newtype instance Functor m => MonadYield a (YieldT a m)

deriving newtype instance MonadReader r m => MonadReader r (YieldT a m)

deriving newtype instance MonadState s m => MonadState s (YieldT a m)

-- | Transforms a 'YieldT''s base monad actions
hoistYieldT
  :: forall m n a r
   . Monad m
  => (forall x. m x -> n x)
  -> YieldT a m r
  -> YieldT a n r
hoistYieldT f (YieldT (CodensityT y0)) = YieldT $ CodensityT \continue ->
  let go = \case
        Pure r -> continue r
        Yield a y -> Yield a (go y)
        Act m -> Act (f $ liftM go $ collapse m)
         where
          collapse mpipe = do
            pipe' <- mpipe
            case pipe' of
              Act mpipe' -> collapse mpipe'
              _ -> pure pipe'
  in  go $ y0 Pure

-- | Runs a 'YieldT' computation in the base monad
runYieldT
  :: forall a b m r
   . Monad m
  => Aggregation m a b
  -- ^ What to do with the values emitted from the stream
  -> YieldT a m r
  -> m (b, r)
  -- ^ The result of aggregating of stream values, and the result of the 'YieldT'
runYieldT agg (YieldT (CodensityT y)) = runYielder agg (y Pure)

data Yielder a m r
  = Yield a (Yielder a m r)
  | Act (m (Yielder a m r))
  | Pure r
  deriving stock (Functor)

instance Functor m => Applicative (Yielder a m) where
  pure = Pure
  (<*>) = ap

instance Functor m => Monad (Yielder a m) where
  p0 >>= f = go p0
   where
    go p = case p of
      Yield a continue -> Yield a $ go continue
      Act m -> Act (go <$> m)
      Pure r -> f r

instance Functor m => MonadYield a (Yielder a m) where
  yield x = Yield x (Pure ())

instance MonadTrans (Yielder a) where
  lift = Act . fmap Pure

instance MonadIO m => MonadIO (Yielder a m) where
  liftIO = lift . liftIO

instance MonadReader r m => MonadReader r (Yielder a m) where
  ask = lift ask
  local f = hoistYielder $ local f

instance MonadState s m => MonadState s (Yielder a m) where
  state = lift . state

hoistYielder
  :: Functor n => (forall x. m x -> n x) -> Yielder a m r -> Yielder a n r
hoistYielder f p0 = go p0
 where
  go = \case
    Yield a p -> Yield a $ go p
    Act m -> Act $ go <$> f m
    Pure r -> Pure r

-- | Runs a 'Yielder' computation in the base monad
runYielder
  :: Monad m
  => Aggregation m a b
  -- ^ What to do with the values emitted from the stream
  -> Yielder a m r
  -> m (b, r)
  -- ^ The result of aggregating of stream values, and the result of the 'Yielder'
runYielder Aggregation {begin, step, done} p0 = begin >>= go p0
 where
  go p x = case p of
    Yield a continue -> (go continue $!) =<< step x a
    Act m -> m >>= \p' -> go p' x
    Pure r -> (,r) <$> done x