> -- | Module: Control.Quiver.Internal
> -- Description: Common definitions
> -- Copyright: © 2015 Patryk Zadarnowski
> -- License: BSD3
> -- Maintainer: pat@jantar.org
> -- Stability: experimental
> -- Portability: portable
> --
> -- This module provides a host of common definitions,
> -- including the main Quiver /processor/ type @P@,
> -- that are reexported by other Quiver modules as
> -- required.
> --
> -- This is the only module in the Quiver library that
> -- exposes the actual four constructors of the stream
> -- processor type @P@, allowing for definition of low
> -- level stream processor transformations, such as
> -- conversions between @P@ and other stream processing
> -- libraries.
> --
> -- As a matter of style, Quiver users should strive to
> -- avoid explicit pattern matching on the @P@ type and
> -- rely instead on the various high level combinators
> -- exported elsewhere, in order to improve chances of
> -- successful deforestation by the various Quiver
> -- rewrite rules.
> {-# LANGUAGE RankNTypes, TupleSections #-}
> module Control.Quiver.Internal (
> P (..), Producer, Consumer, Effect,
> consume, produce, enclose, deliver,
> decouple, deplete,
> qlift, qhoist, qembed,
> ) where
> import Control.Monad.IO.Class
> import Control.Monad.Morph
Data Types
==========
> -- | The main Quiver /stream processor/ type @P a a' b b' f r@,
> -- representing a producer/consumer structure with /bidirectional/,
> -- /bounded/ communication on both the upstream (consumer) and
> -- downstream (producer) channel. The six type parameters have
> -- the following intuitive meaning:
> --
> -- * @a@ is the type of a /request/ values sent by the stream
> -- processor to its upstream partner in order to receive the
> -- next element of the input stream.
> --
> -- * @a'@ is the type of the actual information being consumed
> -- by this stream processor (i.e., elements of its input stream.)
> --
> -- * @b@ is the type of the actual information being produced
> -- by this stream processor (i.e., elements of its output stream.)
> --
> -- * @b'@ is the type of the /response/ values received from
> -- the downstream partner for each element of the output
> -- stream produced by this stream processor.
> --
> -- * @f@ is the type of the stream processor's /base functor/;
> -- usually this is a monad used for stateful stream processing,
> -- exception handling and/or real-world interaction.
> --
> -- * @r@ is the stream processor's /delivery type/, used for
> -- monadic stream processor definition.
> --
> -- Every stream processor is a functor over its delivery type.
> -- However, if the base functor @f@ meets the additional requirements
> -- of 'Applicative' or 'Monad', so will the stream processor itself.
> -- Note that, unlike most other stream processing libraries, @f@
> -- is not required to be a monad in most applications, although
> -- only time will tell whether this generalisation has useful
> -- applications in the real world.
> data P a a' b b' f r =
> -- | @Consume x k q@ represents a /consumer step/, in which
> -- the request @x@ is sent upstream and the returned input
> -- value is supplied to the /continuation processor/ @k@,
> -- or, if the upstream partner has been /depleted/ (i.e.,
> -- delivered its ultimate result, hence reaching the end
> -- of processing), to the /decoupled continuation/ @q@.
> Consume a (a' -> P a a' b b' f r) (Producer b b' f r) |
> -- | @Produce y k q@ represent a /producer step/, in which
> -- the output value @y@ is sent downstream, and the returned
> -- acknowledgement is supplied to the /continuation processor/
> -- @k@, or, if the downstream partner has been /decoupled/
> -- (i.e., delivered its ultimate result, hence reaching the end
> -- of processing), to the /depleted continuation/ @q@.
> Produce b (b' -> P a a' b b' f r) (Consumer a a' f r) |
> -- | @Enclose@ allows for selective application of the base
> -- functor @f@ the the remainder of the computation.
> Enclose (f (P a a' b b' f r)) |
> -- | @Deliver r@ completes processing of information, delivering
> -- its ultimate result @r@.
> Deliver r
> -- | A Quiver /producer/, represented by a stream processor
> -- with unspecified input types.
> type Producer b b' f r = forall a a' . P a a' b b' f r
> -- | A Quiver /consumer/, represented by a stream processor
> -- with unspecified output types.
> type Consumer a a' f r = forall b b' . P a a' b b' f r
> -- | A Quiver /effect/, represented by a stream processor
> -- with unspecified input and output types.
> type Effect f r = forall a a' b b' . P a a' b b' f r
Instances
=========
> instance Functor f => Functor (P a a' b b' f) where
> fmap ff (Consume x k q) = Consume x (fmap ff . k) (fmap ff q)
> fmap ff (Produce y k q) = Produce y (fmap ff . k) (fmap ff q)
> fmap ff (Enclose f) = Enclose (fmap (fmap ff) f)
> fmap ff (Deliver r) = Deliver (ff r)
> r <$ (Consume x k q) = Consume x ((r <$) . k) (r <$ q)
> r <$ (Produce y k q) = Produce y ((r <$) . k) (r <$ q)
> r <$ (Enclose f) = Enclose (fmap (r <$) f)
> r <$ (Deliver _) = Deliver r
> instance Applicative f => Applicative (P a a' b b' f) where
> pure = Deliver
> (Consume x k q) <*> p = Consume x ((<*> p) . k) (q <*> decouple p)
> (Produce y k q) <*> p = Produce y ((<*> p) . k) (q <*> deplete p)
> (Enclose f) <*> p = Enclose (fmap (<*> p) f)
> (Deliver r) <*> p = fmap r p
> instance Monad f => Monad (P a a' b b' f) where
> (Consume x k q) >>= kk = Consume x ((>>= kk) . k) (q >>= decouple . kk)
> (Produce y k q) >>= kk = Produce y ((>>= kk) . k) (q >>= deplete . kk)
> (Enclose f) >>= kk = Enclose (fmap (>>= kk) f)
> (Deliver r) >>= kk = kk r
> fail = Enclose . fail
> instance MonadTrans (P a a' b b') where
> lift = qlift
> instance MonadIO f => MonadIO (P a a' b b' f) where
> liftIO = lift . liftIO
> instance MFunctor (P a a' b b') where
> hoist = qhoist
> instance MMonad (P a a' b b') where
> embed = qembed
Primitive Combinators
=====================
> -- | @consume x k q@ represents a /consumer step/, in which
> -- the request @x@ is sent upstream and the returned input
> -- value is supplied to the /continuation processor/ @k@,
> -- or, if the upstream partner has been /depleted/ (i.e.,
> -- delivered its ultimate result, hence reaching the end
> -- of processing), to the /decoupled continuation/ @q@.
> consume :: a -> (a' -> P a a' b b' f r) -> Producer b b' f r -> P a a' b b' f r
> consume = Consume
> -- | @produce y k q@ represent a /producer step/, in which
> -- the output value @y@ is sent downstream, and the returned
> -- acknowledgement is supplied to the /continuation processor/
> -- @k@, or, if the downstream partner has been /decoupled/
> -- (i.e., delivered its ultimate result, hence reaching the end
> -- of processing), to the /depleted continuation/ @q@.
> produce :: b -> (b' -> P a a' b b' f r) -> Consumer a a' f r -> P a a' b b' f r
> produce = Produce
> -- | @enclose@ allows for selective application of the base
> -- functor @f@ the the remainder of the computation.
> enclose :: f (P a a' b b' f r) -> P a a' b b' f r
> enclose = Enclose
> -- | @deliver r@ completes processing of information, delivering
> -- its ultimate result @r@.
> deliver :: r -> P a a' b b' f r
> deliver = Deliver
Utilities
=========
> -- | @decouple p@ /decouples/ the stream processor @p@, by replacing
> -- the first consumer step in @p@ with that step's decoupled contination,
> -- effectively converting @p@ into a producer processor that no longer
> -- expects to receive any input.
> decouple :: Functor f => P a a' b b' f r -> Producer b b' f r
> decouple (Consume _ _ q) = q
> decouple (Produce y k q) = Produce y (decouple . k) (decouple q)
> decouple (Enclose f) = Enclose (fmap decouple f)
> decouple (Deliver r) = Deliver r
> -- | @deplete p@ /depletes/ the stream processor @p@, by replacing
> -- the first producer step in @p@ with that step's depleted contination,
> -- effectively converting @p@ into a consumer processor that will never
> -- produce any more output.
> deplete :: Functor f => P a a' b b' f r -> Consumer a a' f r
> deplete (Consume x k q) = Consume x (deplete . k) (deplete q)
> deplete (Produce _ _ q) = q
> deplete (Enclose f) = Enclose (fmap deplete f)
> deplete (Deliver r) = Deliver r
Generalized Transformers
========================
> -- | Lifts the value of a base functor into a stream processor;
> -- same as 'lift' from 'MonadTrans', but relaxing constraint
> -- on the base structure from 'Monad' to 'Functor'.
> qlift :: Functor f => f r -> P a a' b b' f r
> qlift = enclose . fmap deliver
> -- | Morphs the value of a base functor into another
> -- functor by applying the supplied functor morphism to every
> -- 'Enclose' step of a stream processor; same as 'hoist' from
> -- 'MFunctor' but relaxing the constraint on the base structure
> -- from 'Monad' to 'Functor'.
> qhoist :: Functor f => (forall x . f x -> g x) -> P a a' b b' f r -> P a a' b b' g r
> qhoist ff = loop
> where
> loop (Consume x k q) = consume x (loop . k) (qhoist ff q)
> loop (Produce y k q) = produce y (loop . k) (qhoist ff q)
> loop (Enclose f) = enclose (ff (fmap loop f))
> loop (Deliver r) = deliver r
> -- | Embeds a monad within another monad transformer;
> -- same as 'embed' from 'MMonad'.
> qembed :: Monad g => (forall x . f x -> P a a' b b' g x) -> P a a' b b' f r -> P a a' b b' g r
> qembed ff = loop
> where
> loop (Consume x k q) = consume x (loop . k) (decouple $ qembed ff q)
> loop (Produce y k q) = produce y (loop . k) (deplete $ qembed ff q)
> loop (Enclose f) = ff f >>= loop
> loop (Deliver r) = deliver r