-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | boxes -- -- concurrent, effectful boxes @package box @version 0.2.0 -- | commit module Box.Committer -- | a Committer a "commits" values of type a. A Sink and a Consumer are -- some other metaphors for this. -- -- A Committer absorbs the value being committed; the value disappears -- into the opaque thing that is a Committer from the pov of usage. newtype Committer m a Committer :: (a -> m Bool) -> Committer m a [commit] :: Committer m a -> a -> m Bool -- | lift a committer from STM liftC :: MonadConc m => Committer (STM m) a -> Committer m a -- | This is a contramapMaybe, if such a thing existed, as the -- contravariant version of a mapMaybe. See witherable cmap :: Monad m => (b -> m (Maybe a)) -> Committer m a -> Committer m b -- | prism handler handles :: Monad m => ((b -> Constant (First b) b) -> a -> Constant (First b) a) -> Committer m b -> Committer m a instance GHC.Base.Applicative m => GHC.Base.Semigroup (Box.Committer.Committer m a) instance GHC.Base.Applicative m => GHC.Base.Monoid (Box.Committer.Committer m a) instance Data.Functor.Contravariant.Contravariant (Box.Committer.Committer m) instance GHC.Base.Applicative m => Data.Functor.Contravariant.Divisible.Divisible (Box.Committer.Committer m) instance GHC.Base.Applicative m => Data.Functor.Contravariant.Divisible.Decidable (Box.Committer.Committer m) -- | A continuation type. module Box.Cont -- | A continuation similar to ` Control.Monad.ContT` but where the result -- type is swallowed by an existential newtype Cont m a Cont :: (forall r. (a -> m r) -> m r) -> Cont m a [with] :: Cont m a -> forall r. (a -> m r) -> m r -- | sometimes you have no choice but to void it up newtype Cont_ m a Cont_ :: ((a -> m ()) -> m ()) -> Cont_ m a [with_] :: Cont_ m a -> (a -> m ()) -> m () instance GHC.Base.Functor (Box.Cont.Cont_ m) instance GHC.Base.Applicative (Box.Cont.Cont_ m) instance GHC.Base.Monad (Box.Cont.Cont_ m) instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Box.Cont.Cont_ m) instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Box.Cont.Cont_ m a) instance (GHC.Base.Functor m, GHC.Base.Semigroup a, GHC.Base.Monoid a) => GHC.Base.Monoid (Box.Cont.Cont_ m a) instance GHC.Base.Functor (Box.Cont.Cont m) instance GHC.Base.Applicative (Box.Cont.Cont m) instance GHC.Base.Monad (Box.Cont.Cont m) instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Box.Cont.Cont m) instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Box.Cont.Cont m a) instance (GHC.Base.Functor m, GHC.Base.Semigroup a, GHC.Base.Monoid a) => GHC.Base.Monoid (Box.Cont.Cont m a) -- | emit module Box.Emitter -- | an Emitter "emits" values of type a. A Source & a Producer -- (of a's) are the two other alternative but overloaded metaphors out -- there. -- -- An Emitter "reaches into itself" for the value to emit, where itself -- is an opaque thing from the pov of usage. An Emitter is named for its -- main action: it emits. newtype Emitter m a Emitter :: m (Maybe a) -> Emitter m a [emit] :: Emitter m a -> m (Maybe a) -- | lift an STM emitter liftE :: MonadConc m => Emitter (STM m) a -> Emitter m a -- | like a monadic mapMaybe. (See witherable) emap :: Monad m => (a -> m (Maybe b)) -> Emitter m a -> Emitter m b -- | prism handler keeps :: Monad m => ((b -> Constant (First b) b) -> a -> Constant (First b) a) -> Emitter m a -> Emitter m b -- | read parse emitter eRead :: (Functor m, Read a) => Emitter m Text -> Emitter m (Either Text a) -- | attoparsec parse emitter eParse :: Functor m => Parser a -> Emitter m Text -> Emitter m (Either Text a) instance GHC.Base.Functor m => GHC.Base.Functor (Box.Emitter.Emitter m) instance GHC.Base.Applicative m => GHC.Base.Applicative (Box.Emitter.Emitter m) instance GHC.Base.Monad m => GHC.Base.Monad (Box.Emitter.Emitter m) instance (GHC.Base.Monad m, GHC.Base.Alternative m) => GHC.Base.Alternative (Box.Emitter.Emitter m) instance (GHC.Base.Alternative m, GHC.Base.Monad m) => GHC.Base.MonadPlus (Box.Emitter.Emitter m) instance (GHC.Base.Alternative m, GHC.Base.Monad m) => GHC.Base.Semigroup (Box.Emitter.Emitter m a) instance (GHC.Base.Alternative m, GHC.Base.Monad m) => GHC.Base.Monoid (Box.Emitter.Emitter m a) -- | A box is something that commits and emits module Box.Box -- | A Box is a product of a Committer m and an Emitter. Think of a box -- with an incoming wire and an outgoing wire. Now notice that the -- abstraction is reversable: are you looking at two wires from "inside a -- box"; a blind erlang grunt communicating with the outside world via -- the two thin wires, or are you looking from "outside the box"; -- interacting with a black box object. Either way, it's a box. And -- either way, the committer is contravariant and the emitter covariant -- so it forms a profunctor. -- -- a Box can also be seen as having an input tape and output tape, thus -- available for turing and finite-state machine metaphorics. data Box m c e Box :: Committer m c -> Emitter m e -> Box m c e [committer] :: Box m c e -> Committer m c [emitter] :: Box m c e -> Emitter m e -- | lift a box from STM liftB :: MonadConc m => Box (STM m) a b -> Box m a b -- | a profunctor dimapMaybe bmap :: Monad m => (a' -> m (Maybe a)) -> (b -> m (Maybe b')) -> Box m a b -> Box m a' b' instance GHC.Base.Functor m => Data.Profunctor.Unsafe.Profunctor (Box.Box.Box m) instance (GHC.Base.Alternative m, GHC.Base.Monad m) => GHC.Base.Semigroup (Box.Box.Box m c e) instance (GHC.Base.Alternative m, GHC.Base.Monad m) => GHC.Base.Monoid (Box.Box.Box m c e) -- | queues Follows pipes-concurrency module Box.Queue -- | Queue specifies how messages are queued data Queue a Unbounded :: Queue a Bounded :: Int -> Queue a Single :: Queue a Latest :: a -> Queue a Newest :: Int -> Queue a New :: Queue a -- | create an unbounded queue queue :: MonadConc m => (Committer (STM m) a -> m l) -> (Emitter (STM m) a -> m r) -> m (l, r) -- | create an unbounded queue, returning the committer result queueC :: MonadConc m => (Committer (STM m) a -> m l) -> (Emitter (STM m) a -> m r) -> m l -- | create an unbounded queue, returning the emitter result queueE :: MonadConc m => (Committer (STM m) a -> m l) -> (Emitter (STM m) a -> m r) -> m r -- | create an unbounded queue, returning the emitter result queueCM :: MonadConc m => (Committer m a -> m l) -> (Emitter m a -> m r) -> m l -- | create an unbounded queue, returning the emitter result queueEM :: MonadConc m => (Committer m a -> m l) -> (Emitter m a -> m r) -> m r -- | wait for the first action, and then cancel the second waitCancel :: MonadConc m => m b -> m a -> m b -- | create a queue, returning the ends ends :: MonadSTM stm => Queue a -> stm (a -> stm (), stm a) -- | connect a committer and emitter action via spawning a queue, and wait -- for both to complete. withQ :: MonadConc m => Queue a -> (Queue a -> STM m (Box (STM m) a a, STM m ())) -> (Committer (STM m) a -> m l) -> (Emitter (STM m) a -> m r) -> m (l, r) -- | connect a committer and emitter action via spawning a queue, and wait -- for emitter to complete. withQE :: MonadConc m => Queue a -> (Queue a -> STM m (Box (STM m) a a, STM m ())) -> (Committer (STM m) a -> m l) -> (Emitter (STM m) a -> m r) -> m r -- | connect a committer and emitter action via spawning a queue, and wait -- for committer to complete. withQC :: MonadConc m => Queue a -> (Queue a -> STM m (Box (STM m) a a, STM m ())) -> (Committer (STM m) a -> m l) -> (Emitter (STM m) a -> m r) -> m l -- | turn a queue into a box (and a seal) toBox :: MonadSTM stm => Queue a -> stm (Box stm a a, stm ()) -- | run two actions concurrently, but wait and return on the left result. concurrentlyLeft :: MonadConc m => m a -> m b -> m a -- | run two actions concurrently, but wait and return on the right result. concurrentlyRight :: MonadConc m => m a -> m b -> m b -- | plugs box continuations module Box.Plugs -- | hook an emitter action to a queue, creating a committer continuation commitPlug :: (Emitter STM a -> IO ()) -> Cont IO (Committer STM a) -- | hook a committer action to a queue, creating an emitter continuation emitPlug :: (Committer STM a -> IO r) -> Cont IO (Emitter STM a) -- | hook a committer action to a queue, creating an emitter continuation emitPlugM :: (Committer IO a -> IO r) -> Cont IO (Emitter IO a) -- | create a double-queued box plug boxPlug :: (Emitter STM a -> IO ()) -> (Committer STM b -> IO ()) -> Cont IO (Box STM a b) -- | create a box plug from a box action. Caution: implicitly, this (has -- to) forget interactions between emitter and committer in the one -- action (and it does so silently). These forgotten interactions are -- typically those that create races boxForgetPlug :: (Box STM b a -> IO ()) -> Cont IO (Box STM a b) -- | various ways to connect things up module Box.Connectors -- | fuse an emitter directly to a committer fuse_ :: Monad m => Emitter m a -> Committer m a -> m () -- | slightly more efficient version fuseSTM_ :: MonadConc m => Emitter (STM m) a -> Committer (STM m) a -> m () -- | fuse a box -- --
-- (fuse (pure . Just) $ liftB <$> (Box <$> cStdout 2 <*> emitter')) >> sleep 1 ---- -- hi bye -- --
-- etc () (Transducer id) == fuse (pure . pure) . fmap liftB --fuse :: Monad m => (a -> m (Maybe b)) -> Cont m (Box m b a) -> m () -- | fuse a box with an STM mapMaybe action fuseSTM :: MonadConc m => (a -> STM m (Maybe b)) -> Cont m (Box (STM m) b a) -> m () -- | fuse-branch an emitter forkEmit :: Monad m => Emitter m a -> Committer m a -> Emitter m a -- | a box modifier that feeds commits back to the emitter feedback :: MonadConc m => (a -> m (Maybe b)) -> Cont m (Box m b a) -> Cont m (Box m b a) -- | an emitter post-processor that cons transformed emissions back into -- the emitter feedbackE :: MonadConc m => (a -> m (Maybe a)) -> Emitter m a -> Cont m (Emitter m a) -- | fuse an emitter to a buffer fuseEmit :: MonadConc m => Emitter (STM m) a -> Cont m (Emitter (STM m) a) -- | fuse an emitter to a buffer fuseEmitM :: MonadConc m => Emitter m a -> Cont m (Emitter m a) -- | fuse a committer to a buffer fuseCommit :: MonadConc m => Committer (STM m) a -> Cont m (Committer (STM m) a) -- | fuse a committer to a buffer fuseCommitM :: MonadConc m => Committer m a -> Cont m (Committer m a) -- | merge two emitters -- -- This differs from `liftA2 (<>)` in that the monoidal (and -- alternative) instance of an Emitter is left-biased (The left emitter -- exhausts before the right one is begun). This merge is concurrent. emerge :: MonadConc m => Cont m (Emitter (STM m) a, Emitter (STM m) a) -> Cont m (Emitter (STM m) a) -- | monadic version emergeM :: MonadConc m => Cont m (Emitter m a, Emitter m a) -> Cont m (Emitter m a) -- | split a committer splitCommit :: MonadConc m => Cont m (Committer m a) -> Cont m (Either (Committer m a) (Committer m a)) -- | split a committer (STM m) splitCommitSTM :: MonadConc m => Cont m (Committer (STM m) a) -> Cont m (Either (Committer (STM m) a) (Committer (STM m) a)) -- | use a split committer contCommit :: Either (Committer m a) (Committer m b) -> (Committer m a -> Committer m b) -> Committer m b -- | This module is experimental and may not work. module Box.Broadcast -- | a broadcaster newtype Broadcaster m a Broadcaster :: TVar m (Committer m a) -> Broadcaster m a [unBroadcast] :: Broadcaster m a -> TVar m (Committer m a) -- | create a (broadcaster, committer) broadcast :: MonadSTM stm => stm (Broadcaster stm a, Committer stm a) -- | subscribe to a broadcaster subscribe :: MonadConc m => Broadcaster (STM m) a -> Cont m (Emitter (STM m) a) -- | a funneler newtype Funneler m a Funneler :: TVar m (Emitter m a) -> Funneler m a [unFunnel] :: Funneler m a -> TVar m (Emitter m a) -- | create a (funneler, emitter) funnel :: MonadSTM stm => stm (Funneler stm a, Emitter stm a) -- | widen to a funneler widen :: MonadConc m => Funneler (STM m) a -> Cont m (Committer (STM m) a) -- | Streaming functionality module Box.Stream -- | turn an emitter into a stream toStream :: MonadConc m => Emitter (STM m) a -> Stream (Of a) m () -- | turn a stream into a committer fromStream :: MonadConc m => Stream (Of b) m () -> Committer (STM m) b -> m () -- | create a committer from a stream consumer toCommit :: MonadConc m => (Stream (Of a) m () -> m r) -> Cont m (Committer (STM m) a) -- | create a committer from a fold toCommitFold :: MonadConc m => FoldM m a () -> Cont m (Committer (STM m) a) -- | create a committer from a sink toCommitSink :: MonadConc m => (a -> m ()) -> Cont m (Committer (STM m) a) -- | create an emitter from a stream toEmit :: MonadConc m => Stream (Of a) m () -> Cont m (Emitter (STM m) a) -- | insert a queue into a stream (left biased collapse) todo: look at -- biases queueStream :: MonadConc m => Stream (Of a) m () -> Cont m (Stream (Of a) m ()) -- | turn an emitter into a stream toStreamM :: MonadConc m => Emitter m a -> Stream (Of a) m () -- | turn a stream into a committer fromStreamM :: MonadConc m => Stream (Of b) m () -> Committer m b -> m () -- | timing effects module Box.Time -- | sleep for x seconds sleep :: MonadConc m => Double -> m () -- | keeping a box open sometimes needs a long running emitter keepOpen :: MonadConc m => Cont m (Emitter (STM m) a) -- | a stream with suggested delays. DiffTime is the length of time to wait -- since the start of the stream > delayTimed (S.each (zip -- (fromIntegral $ [1..10]) [1..10])) |> S.print delayTimed :: (MonadConc m, MonadIO m) => Stream (Of (NominalDiffTime, a)) m () -> Stream (Of a) m () -- | A value with a timestamp annotation. data Stamped a Stamped :: UTCTime -> a -> Stamped a [timestamp] :: Stamped a -> UTCTime [value] :: Stamped a -> a -- | Add the current time stampNow :: (MonadConc m, MonadIO m) => a -> m (Stamped a) -- | adding a time stamp todo: how to do this properly? emitStamp :: (MonadConc m, MonadIO m) => Cont m (Emitter m a) -> Cont m (Emitter m (Stamped a)) instance GHC.Read.Read a => GHC.Read.Read (Box.Time.Stamped a) instance GHC.Show.Show a => GHC.Show.Show (Box.Time.Stamped a) instance GHC.Classes.Eq a => GHC.Classes.Eq (Box.Time.Stamped a) -- | transduce module Box.Transducer -- | transduction wiki says: "A transducer is a device that converts -- energy from one form to another." Translated to context, this -- Transducer converts a stream of type a to a stream of a different -- type. newtype Transducer s a b Transducer :: (forall m. Monad m => Stream (Of a) (StateT s m) () -> Stream (Of b) (StateT s m) ()) -> Transducer s a b [transduce] :: Transducer s a b -> forall m. Monad m => Stream (Of a) (StateT s m) () -> Stream (Of b) (StateT s m) () -- | emit - transduce - commit -- -- with etc, you're in the box, and inside the box, there are no effects: -- just a stream of a's, pure functions and state tracking. It's a nice -- way to code, and very friendly for the compiler. When the committing -- and emitting is done, the box collapses to state. -- -- The combination of an input tape, an output tape, and a state-based -- stream computation lends itself to the etc computation as a -- finite-state transducer or mealy machine. etc :: MonadConc m => s -> Transducer s a b -> Cont m (Box (STM m) b a) -> m s -- | Monadic version of etc. etcM :: (MonadConc m, MonadBase m m) => s -> Transducer s a b -> Cont m (Box m b a) -> m s -- | convert a Pipe to a Transducer asPipe :: Monad m => Pipe a b (StateT s m) () -> Stream (Of a) (StateT s m) () -> Stream (Of b) (StateT s m) () instance Control.Category.Category (Box.Transducer.Transducer s) -- | IO actions module Box.IO -- | a single stdin committer action cStdin_ :: Committer (STM IO) Text -> IO () -- | a finite stdin committer action cStdin :: Int -> Committer (STM IO) Text -> IO () -- | a forever stdin committer action cStdin' :: Committer (STM IO) Text -> IO () -- | a Cont stdin emitter eStdin :: Int -> Cont IO (Emitter (STM IO) Text) -- | read from console, throwing away read errors readStdin :: Read a => Cont IO (Emitter (STM IO) a) -- | a single stdout emitter action eStdout_ :: Emitter (STM IO) Text -> IO () -- | a finite stdout emitter action eStdout :: Int -> Emitter (STM IO) Text -> IO () -- | a finite stdout emitter action eStdoutM :: Int -> Emitter IO Text -> IO () -- | a forever stdout emitter action eStdout' :: Emitter (STM IO) Text -> IO () -- | a Cont stdout committer cStdout :: Int -> Cont IO (Committer (STM IO) Text) -- | show to stdout showStdout :: Show a => Cont IO (Committer (STM IO) a) -- | console box > etc () (Trans $ s -> s & S.takeWhile (/="q") -- & S.map ("echo: " <>)) (console 5) consolePlug :: Int -> Cont IO (Box (STM IO) Text Text) -- | emit lines from a file emitLines :: FilePath -> Cont IO (Emitter (STM IO) Text) -- | commit lines to a file commitLines :: FilePath -> Cont IO (Committer (STM IO) Text) -- | commit to a list CRef cCRef :: MonadConc m => m (IORef m [b], Cont m (Committer (STM m) b), m [b]) -- | commit to a monoidal CRef cCRefM :: (MonadConc m, Monoid a) => m (IORef m a, Cont m (Committer (STM m) a), m a) -- | fold an emitter through a transduction, committing to a list toListM :: MonadConc m => Cont m (Emitter (STM m) a) -> s -> Transducer s a b -> m ([b], s) -- | get all commissions as a list getCommissions :: MonadConc m => Cont m (Emitter (STM m) a) -> s -> Transducer s a b -> m [b] -- | get all emissions getEmissions :: MonadConc m => Int -> Cont m (Emitter (STM m) a) -> m [a] -- | Boxes that emit, transduce & commit -- -- This library follows the ideas and code from pipes-concurrency -- and mvc but with some polymorphic tweaks and definitively more -- pretentious names. module Box -- | & is a reverse application operator. This provides -- notational convenience. Its precedence is one higher than that of the -- forward application operator $, which allows & to be -- nested in $. -- --
-- >>> 5 & (+1) & show -- "6" --(&) :: () => a -> (a -> b) -> b infixl 1 & -- | An example of a Box for the command line. module Box.Control -- | request ADT data ControlRequest Check :: ControlRequest Stop :: ControlRequest Quit :: ControlRequest Start :: ControlRequest Reset :: ControlRequest Kill :: ControlRequest -- | response ADT data ControlResponse ShutDown :: ControlResponse On :: Bool -> ControlResponse Log :: Text -> ControlResponse -- | A Box that communicates via ControlRequest and -- ControlResponse type ControlBox m = (MonadConc m) => Cont m (Box (STM m) ControlResponse ControlRequest) -- | Should the box be kept alive? data ControlConfig KeepAlive :: Double -> ControlConfig AllowDeath :: ControlConfig -- | Defauilt is to let the box die. defaultControlConfig :: ControlConfig -- | a command-line control box. consoleControlBox :: ControlBox IO -- | Parse command line requests parseControlRequest :: Parser ControlRequest -- | an effect that can be started and stopped controlBox :: IO a -> Box (STM IO) ControlResponse ControlRequest -> IO () -- | send Start, wait for a Ready signal, run action, wait x secs, then -- send Quit testBox :: IO () -- | A box with a self-destruct timer. timeOut :: Double -> ControlBox m instance GHC.Classes.Eq Box.Control.ControlConfig instance GHC.Show.Show Box.Control.ControlConfig instance GHC.Generics.Generic Box.Control.ControlResponse instance Data.Data.Data Box.Control.ControlResponse instance GHC.Classes.Eq Box.Control.ControlResponse instance GHC.Read.Read Box.Control.ControlResponse instance GHC.Show.Show Box.Control.ControlResponse instance GHC.Generics.Generic Box.Control.ControlRequest instance Data.Data.Data Box.Control.ControlRequest instance GHC.Classes.Eq Box.Control.ControlRequest instance GHC.Read.Read Box.Control.ControlRequest instance GHC.Show.Show Box.Control.ControlRequest -- | based on -- https://github.com/Gabriel439/Haskell-MVC-Updates-Library module Box.Updater -- | An updater of a value a, where the updating process consists of an IO -- fold over an emitter data Updater a Updater :: FoldM IO e a -> Cont IO (Emitter STM e) -> Updater a -- | Create an Updater value using a pure Fold updater :: Fold e a -> Cont IO (Emitter STM e) -> Updater a -- | run an action on each update > listen mempty = id > > listen -- (f <> g) = listen g . listen f listen :: (a -> IO ()) -> Updater a -> Updater a -- | Convert an Updater to an Emitter continuation. updates :: Updater a -> Cont IO (Emitter STM a) instance GHC.Base.Functor Box.Updater.Updater instance GHC.Base.Applicative Box.Updater.Updater