-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Communicating Haskell Processes: an implementation of concurrency ideas from Communicating Sequential Processes -- -- Requires at least GHC 6.8.1 @package chp @version 1.0.0 module Control.Concurrent.CHP.Traces.TraceOff -- | A trace type that does not record anything. data TraceOff instance Trace TraceOff instance Show TraceOff -- | This module contains support for CSP-style tracing. A CSP trace is -- simply a flat list of events in the order in which they occurred. module Control.Concurrent.CHP.Traces.CSP -- | A classic CSP trace. It is simply the channel labels, and a list of -- recorded events in sequence -- the head of the list is the first -- (oldest) event. newtype CSPTrace CSPTrace :: (ChannelLabels, [RecordedEvent]) -> CSPTrace runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace) runCHP_CSPTraceAndPrint :: CHP a -> IO () instance Trace CSPTrace instance Show CSPTrace -- | This module contains support for structural traces. Structural traces -- reflect the parallel composition of your program. Effectively, each -- process records its own local trace. Parallel traces are then merged -- as parallel compositions, and you end up with a big tree of -- sequentially and parallel-composed traces. Note that in this tracing -- style, unlike CSP and VCR, events are recorded by every process -- involved in them, not just once per occurrence. module Control.Concurrent.CHP.Traces.Structural -- | A nested (or hierarchical) trace. The trace is an event hierarchy, -- wrapped in a Maybe type to allow for representation of the empty trace -- (Nothing). newtype StructuralTrace StructuralTrace :: (ChannelLabels, Maybe (EventHierarchy RecordedIndivEvent)) -> StructuralTrace -- | A data type representing a hierarchy of events. The count on the -- StructuralSequence count is a replicator count for that list of -- sequential items. data EventHierarchy a SingleEvent :: a -> EventHierarchy a StructuralSequence :: Int -> [EventHierarchy a] -> EventHierarchy a StructuralParallel :: [EventHierarchy a] -> EventHierarchy a runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace) runCHP_StructuralTraceAndPrint :: CHP a -> IO () -- | Flattens the events into a list. The resulting list may contain -- duplicates, and it should not be assumed that the order relates in any -- way to the original hierarchy. getAllEventsInHierarchy :: EventHierarchy a -> [a] instance Trace StructuralTrace instance Show StructuralTrace instance Functor EventHierarchy -- | A module for recording View Centric Reasoning (VCR) traces. A view -- centric reasnoning trace is a list of sets of events. Each set -- contains independent events that have no causal relationship between -- them. Hopefully we will publish a paper explaining all this in detail -- soon. module Control.Concurrent.CHP.Traces.VCR -- | A VCR (View-Centric Reasoning) trace. It is the channel labels, -- accompanied by a sequential list of sets of recorded events. Each of -- the sets is a set of independent events. The set at the head of the -- list is the first-recorded (oldest). newtype VCRTrace VCRTrace :: (ChannelLabels, [Set RecordedEvent]) -> VCRTrace runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace) runCHP_VCRTraceAndPrint :: CHP a -> IO () instance Trace VCRTrace instance Show VCRTrace -- | A module that re-exports all the parts of the library related to -- tracing. -- -- The idea of tracing is to record the concurrent events (i.e. channel -- communications and barrier synchronisations) that occur during a run -- of the program. You can think of it as automatically turning on a lot -- of debug logging. -- -- Typically, at the top-level of your program you should have: -- --
-- main :: IO () -- main = runCHP myMainProcess ---- -- To turn on the tracing mechanism of your choice (for example, -- CSP-style tracing) to automatically print out the trace after -- completion of your program, just use the appropriate helper function: -- --
-- main :: IO () -- main = runCHP_CSPTraceAndPrint myMainProcess ---- -- It could hardly be easier. If you want more fine-grained control and -- examination of the trace, you can use the helper functions of the form -- runCHP_CSPTrace that give back a data structure representing -- the trace, which you can then manipulate. The Doc used by the traces -- is from the Text.PrettyPrint.HughesPJ module that currently -- comes with GHC (I think). module Control.Concurrent.CHP.Traces -- | A globally recorded event, found in CSP and VCR traces. Either a -- channel communication (with a unique identifier of the channel) or a -- barrier synchronisation (with a unique identifier of the barrier). The -- identifiers are per channel/barrier, not per event. Currently, -- channels and barriers can never have the same Unique as each other, -- but do not rely on this behaviour. data RecordedEvent ChannelComm :: Unique -> RecordedEvent BarrierSync :: Unique -> RecordedEvent -- | An individual record of an event, found in nested traces. Either a -- channel write or read, or a barrier synchronisation, each with a -- unique identifier for the barrier/channel. The event will be recorded -- by everyone involved, and a ChannelWrite will have the same channel -- identifier as the corresponding channel-read. The identifiers are per -- channel/barrier, not per event. Currently, channels and barriers can -- never have the same Unique as each other, but do not rely on this -- behaviour. data RecordedIndivEvent ChannelWrite :: Unique -> RecordedIndivEvent ChannelRead :: Unique -> RecordedIndivEvent BarrierSyncIndiv :: Unique -> RecordedIndivEvent -- | A class representing a type of trace. The main useful function is -- runCHPAndTrace, but because its type is only determined by its -- return type, you may wish to use the already-typed functions offered -- in each trace module -- see the modules in -- Control.Concurrent.CHP.Traces. class (Show t) => Trace t runCHPAndTrace :: (Trace t) => CHP a -> IO (Maybe a, t) emptyTrace :: (Trace t) => t prettyPrint :: (Trace t) => t -> Doc module Control.Concurrent.CHP.Parallel -- | This type-class supports parallel composition of processes. You may -- use the runParallel function to run a list of processes, or the -- <||> operator to run just a pair of processes. -- -- In each case, the composition waits for all processes to finish, -- either successfully or with poison. At the end of this, if any -- process exited with poison, the composition will rethrow this -- poison. If all the processes completed successfully, the results will -- be returned. If you want to ignore poison from the sub-processes, use -- an empty poison handler and onPoisonTrap with each branch. -- -- Runs the given list of processes in parallel, and then returns a list -- of the results, in the same order as the processes given. Will only -- return when all the inner processes have completed. runParallel :: [CHP a] -> CHP [a] -- | Runs all the given processes in parallel and discards any output. Does -- not return until all the processes have completed. runParallel_ ps is -- effectively equivalent to runParallel ps >> return (). runParallel_ :: [CHP a] -> CHP () -- | A useful operator for performing a two process equivalent of -- runParallel that gives the return values back as a pair rather than a -- list. This also allows the values to have different types (<||>) :: CHP a -> CHP b -> CHP (a, b) -- | A monad transformer used for introducing forking blocks. data (Monad m, MonadCHP m) => ForkingT m a -- | Executes a forking block. Processes may be forked off inside (using -- the fork function). When the block completes, it waits for all -- the forked off processes to complete before returning the output, as -- long as none of the processes terminated with uncaught poison. If they -- did, the poison is propagated (rethrown). forking :: (MonadCHP m) => ForkingT m a -> m a -- | Forks off the given process. The process then runs in parallel with -- this code, until the end of the forking block, when all forked-off -- processes are waited for. At that point, once all of the processes -- have finished, if any of them threw poison it is propagated. fork :: (MonadCHP m) => CHP () -> ForkingT m () instance (Monad m) => Monad (ForkingT m) instance (MonadIO m) => MonadIO (ForkingT m) instance MonadTrans ForkingT instance (MonadCHP m) => MonadCHP (ForkingT m) -- | This module contains all the central monads in the CHP library. module Control.Concurrent.CHP.Monad -- | The central monad of the library. You can use runCHP and -- runCHP_ to execute programs in this monad. data CHP a -- | A monad transformer class that is very similar to MonadIO. This can be -- useful if you want to add monad transformers (such as StateT, ReaderT) -- on top of the CHP monad. class (MonadIO m) => MonadCHP m liftCHP :: (MonadCHP m) => CHP a -> m a -- | Runs a CHP program. You should use this once, at the top-level of your -- program. Do not ever use this function twice in parallel and attempt -- to communicate between those processes using channels. Instead, run -- this function once and use it to spawn off the parallel processes that -- you need. runCHP :: CHP a -> IO (Maybe a) -- | Runs a CHP program. Like runCHP but discards the output. runCHP_ :: CHP a -> IO () -- | Allows you to provide a handler for sections with poison. It is -- usually used in an infix form as follows: -- --
-- (readChannel c >>= writeChannel d) `onPoison` (poison c >> poison d) ---- -- It handles the poison and does not rethrow it (unless your handler -- does so). If you want to rethrow (and actually, you'll find you -- usually do), use onPoisonRethrow onPoisonTrap :: CHP a -> CHP a -> CHP a -- | Like onPoisonTrap, this function allows you to provide a -- handler for poison. The difference with this function is that even if -- the poison handler does not throw, the poison exception will always be -- re-thrown after the handler anyway. That is, the following lines of -- code all have identical behaviour: -- --
-- foo -- foo `onPoisonRethrow` throwPoison -- foo `onPoisonRethrow` return () --onPoisonRethrow :: CHP a -> CHP () -> CHP a -- | Throws a poison exception. throwPoison :: CHP a -- | A class indicating that something is poisonable. class Poisonable c poison :: (Poisonable c, MonadCHP m) => c -> m () -- | Poisons all the given items. A handy shortcut for mapM_ -- poison. poisonAll :: (Poisonable c, MonadCHP m) => [c] -> m () -- | A monad transformer for easier looping. This is independent of the CHP -- aspects, but has all the right type-classes defined for it to make it -- easy to use with the CHP library. data (Monad m) => LoopWhileT m a -- | Runs the given action in a loop, executing it repeatedly until a -- while statement inside it has a False condition. If you use -- loop without while, the effect is the same as -- forever. loop :: (Monad m) => LoopWhileT m a -> m () -- | Continues executing the loop if the given value is True. If the value -- is False, the loop is broken immediately, and control jumps back to -- the next action after the outer loop statement. Thus you can -- build pre-condition, post-condition, and mid-condition loops, -- placing the condition wherever you like. while :: (Monad m) => Bool -> LoopWhileT m () -- | The classic skip process/guard. Does nothing, and is always ready. -- -- Suitable for use in an alt. skip :: CHP () -- | The stop guard. Its main use is that it is never ready in a choice, so -- can be used to mask out guards. If you actually execute stop, that -- process will do nothing more. Any parent process waiting for it to -- complete will wait forever. stop :: CHP () -- | Waits for the specified number of microseconds (millionths of a -- second). There is no guaranteed precision, but the wait will never -- complete in less time than the parameter given. -- -- Suitable for use in an alt, but note that waitFor 0 is not -- the same as skip. waitFor 0 </> x will not -- always select the first guard, depending on x. Included in this is the -- lack of guarantee that waitFor 0 </> -- waitFor n will select the first guard for any value of n -- (including 0). It is not useful to use two waitFor guards in a single -- alt anyway. waitFor :: Int -> CHP () instance (MonadError e m) => MonadError e (LoopWhileT m) instance (MonadCHP m) => MonadCHP (LoopWhileT m) instance (MonadIO m) => MonadIO (LoopWhileT m) instance MonadTrans LoopWhileT instance (Monad m) => Monad (LoopWhileT m) -- | A module with support for things that are enrollable (barriers and -- broadcast channels). module Control.Concurrent.CHP.Enroll -- | An enrolled wrapper for barriers that shows at the type-level whether -- you are enrolled on a barrier. Enrolled Barriers should never be -- passed to two (or more) processes running in parallel; if two -- processes synchronise based on a single enroll call, undefined -- behaviour will result. data Enrolled b a class Enrollable b z enroll :: (Enrollable b z) => b z -> (Enrolled b z -> CHP a) -> CHP a resign :: (Enrollable b z) => Enrolled b z -> CHP a -> CHP a -- | Like enroll but enrolls on the given pair of barriers enrollPair :: (Enrollable b p, Enrollable b' p') => (b p, b' p') -> ((Enrolled b p, Enrolled b' p') -> CHP a) -> CHP a -- | Like enroll but enrolls on the given list of barriers enrollList :: (Enrollable b p) => [b p] -> ([Enrolled b p] -> CHP a) -> CHP a -- | A module containing the ALT constructs. An ALT (a term inherited from -- occam) is a choice between several events. In CHP, we say that an -- event must support alting to be a valid choice. Events that do -- support alting are: -- --
-- liftM Just (readChannel c) <-> (waitFor 1000000 >> return Nothing) ---- --
-- liftM Just (readChannel c) </> (skip >> return Nothing) ---- --
-- readChannel c0 <-> readChannel c1 ---- --
-- (readChannel c >>= writeChannel d) </> skip --module Control.Concurrent.CHP.Alt -- | An alt between several actions, with arbitrary priority. The first -- available action is chosen (with an arbitrary choice if many guards -- are available at the same time), its body run, and its value returned. alt :: [CHP a] -> CHP a -- | A useful operator to perform an alt. This operator is -- associative, and has arbitrary priority. When you have lots of guards, -- it is probably easier to use the alt function. alt -- may be more efficent than foldl1 (<->) (<->) :: CHP a -> CHP a -> CHP a -- | An alt between several actions, with arbitrary priority. The first -- available action is chosen (biased towards actions nearest the -- beginning of the list), its body run, and its value returned. priAlt :: [CHP a] -> CHP a -- | A useful operator to perform a priAlt. This operator is -- associative, and has descending priority (that is, it is left-biased). -- When you have lots of actions, it is probably easier to use the -- priAlt function. priAlt may be more efficent than -- foldl1 (</>) (>) :: CHP a -> CHP a -> CHP a -- | A module containing barriers. -- -- A barrier is a synchronisation primitive. When N processes are -- enrolled on a barrier, all N must synchronise on the barrier before -- any synchronisations may complete, at which point they all complete. -- That is, when a single process synchronises on a barrier, it must then -- wait until all the other enrolled processes also synchronise before it -- can finish. -- -- Only processes enrolled on a barrier may synchronise on it. Enrolled -- barriers should not be passed around between processes, or used twice -- in a parallel composition. Instead, each process should enroll on the -- barrier itself. -- -- Barriers support choice (alting). This can lead to a lot of -- non-determinism and some confusion. Consider these two processes, both -- enrolled on barriers a and b: -- --
-- (sync a <-> sync b) -- (sync b <-> sync a) ---- -- Which barrier completes is determined by the run-time, and will be an -- arbitrary choice. This is even the case when priority is involved: -- --
-- (sync a </> sync b) -- (sync b </> sync a) ---- -- Clearly there is no way to resolve this to satisfy both priorities; -- the run-time will end up choosing. -- -- Barrier poison can be detected when syncing, enrolling or resigning. -- You may only poison a barrier that you are currently enrolled on. -- -- Barriers can also support phases. The idea behind a phased barrier is -- that a barrier is always on a certain phase P. Whenever a barrier -- successfully completes, the phase is incremented (but it does not have -- to be an integer). Everyone is told the new phase once they complete a -- synchronisation, and may query the current phase for any barrier that -- they are currently enrolled on. module Control.Concurrent.CHP.Barriers -- | A special case of the PhasedBarrier that has no useful phases, i.e. a -- standard barrier. type Barrier = PhasedBarrier () -- | Creates a new barrier with no processes enrolled newBarrier :: CHP Barrier -- | Creates a new barrier with no processes enrolled and labels it in -- traces using the given label newBarrierWithLabel :: String -> CHP Barrier -- | A phased barrier that is capable of being poisoned and throwing -- poison. You will need to enroll on it to do anything useful with it. -- For the phases you can use any type that satisfies Enum, Bounded and -- Eq. The phase increments every time the barrier completes. -- Incrementing consists of: if p == maxBound then minBound else succ -- p. Examples of things that make sense for phases: -- --
-- (a, b, c, d, e) <- newChannels ---- -- To create five channels of the same type. class ChannelTuple t newChannels :: (ChannelTuple t, MonadCHP m) => m t -- | Creates a list of channels of the same type with the given length. If -- you need to access some channels by index, use this function. -- Otherwise you may find using newChannels to be easier. newChannelList :: (Channel r w, MonadCHP m) => Int -> m [Chan r w a] -- | A helper that is like newChannelList, but labels the channels -- with the given list. The number of channels returned is the same as -- the length of the list of labels newChannelListWithLabels :: (Channel r w, MonadCHP m) => [String] -> m [Chan r w a] -- | A helper that is like newChannelList, but labels the channels -- according to a pattern. Given a stem such as foo, it names the -- channels in the list foo0, foo1, foo2, etc. newChannelListWithStem :: (Channel r w, MonadCHP m) => Int -> String -> m [Chan r w a] -- | Gets the channel's identifier. Useful if you need to be able to -- identify a channel in the trace later on. getChannelIdentifier :: Chan r w a -> Unique -- | A reading channel-end type that allows poison to be thrown data Chanin a -- | A writing channel-end type that allows poison to be thrown data Chanout a -- | Gets the reading end of a channel from its Chan type. reader :: Chan r w a -> r a -- | Gets the writing end of a channel from its Chan type. writer :: Chan r w a -> w a -- | Gets all the reading ends of a list of channels. A shorthand for -- map reader. readers :: [Chan r w a] -> [r a] -- | Gets all the writing ends of a list of channels. A shorthand for -- map writer. writers :: [Chan r w a] -> [w a] -- | A class indicating that a channel can be read from. class ReadableChannel chanEnd readChannel :: (ReadableChannel chanEnd) => chanEnd a -> CHP a extReadChannel :: (ReadableChannel chanEnd) => chanEnd a -> (a -> CHP b) -> CHP b -- | A class indicating that a channel can be written to. class WriteableChannel chanEnd writeChannel :: (WriteableChannel chanEnd) => chanEnd a -> a -> CHP () extWriteChannel :: (WriteableChannel chanEnd) => chanEnd a -> CHP a -> CHP () -- | Claims the given channel-end, executes the given block, then releases -- the channel-end and returns the output value. If poison or an IO -- exception is thrown inside the block, the channel is released and the -- poison/exception re-thrown. claim :: Shared c a -> (c a -> CHP b) -> CHP b -- | A wrapper (usually around a channel-end) indicating that the inner -- item is shared. Use the claim function to use this type. data Shared c a type OneToOneChannel = Chan Chanin Chanout oneToOneChannel :: (MonadCHP m) => m (OneToOneChannel a) type OneToAnyChannel = Chan (Shared Chanin) (Chanout) oneToAnyChannel :: (MonadCHP m) => m (OneToAnyChannel a) type AnyToOneChannel = Chan (Chanin) (Shared Chanout) anyToOneChannel :: (MonadCHP m) => m (AnyToOneChannel a) type AnyToAnyChannel = Chan (Shared Chanin) (Shared Chanout) anyToAnyChannel :: (MonadCHP m) => m (AnyToAnyChannel a) instance Channel (Shared Chanin) (Shared Chanout) instance Channel Chanin (Shared Chanout) instance Channel (Shared Chanin) Chanout instance Channel Chanin Chanout instance ChanoutC STMChannel a instance ChaninC STMChannel a instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a, Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a, Chan r w a) instance (Channel r w) => ChannelTuple (Chan r w a, Chan r w a) instance Poisonable (Chanout a) instance Poisonable (Chanin a) instance WriteableChannel Chanout instance ReadableChannel Chanin -- | A module containing broadcast channels (one-to-many). Whereas a -- one-to-any channel features one writer sending a single value -- to one (of many) readers, a one-to-many channel features one -- writer sending the same value to many readers. So a -- one-to-any channel involves claiming the channel-end to ensure -- exclusivity, but a one-to-many channel involves enrolling on the -- channel-end (subscribing) before it can engage in communication. -- -- A communication on a one-to-many channel only takes place when the -- writer and all readers currently enrolled agree to communicate. What -- happens when the writer wants to communicate and no readers are -- enrolled is undefined (the writer may block, or may communicate -- happily to no-one). module Control.Concurrent.CHP.BroadcastChannels -- | The reading end of a broadcast channel. You must enroll on it before -- you can read from it or poison it. data BroadcastChanin a -- | The writing end of a broadcast channel. data BroadcastChanout a type OneToManyChannel = Chan BroadcastChanin BroadcastChanout type AnyToManyChannel = Chan BroadcastChanin (Shared BroadcastChanout) oneToManyChannel :: CHP (OneToManyChannel a) anyToManyChannel :: CHP (AnyToManyChannel a) instance Enum Phase instance Bounded Phase instance Eq Phase instance Channel BroadcastChanin (Shared BroadcastChanout) instance Channel BroadcastChanin BroadcastChanout instance Poisonable (Enrolled BroadcastChanin a) instance Poisonable (BroadcastChanout a) instance ReadableChannel (Enrolled BroadcastChanin) instance WriteableChannel BroadcastChanout instance Enrollable BroadcastChanin a -- | This module re-exports the core functionality of the CHP library. -- Other modules that you also may wish to import are: -- --
-- import qualified Control.Concurrent.CHP.Common as Common ---- -- or: -- --
-- import qualified Control.Concurrent.CHP.Common as CHP ---- -- to circumvent this problem. module Control.Concurrent.CHP.Common -- | Forever forwards the value onwards, unchanged. Adding this to your -- process network effectively adds a single-place buffer. id :: Chanin a -> Chanout a -> CHP () -- | Forever forwards the value onwards, in an extended rendezvous. This is -- like id but does not add any buffering to your network. -- -- extId is a unit of the associative operator |->|. extId :: Chanin a -> Chanout a -> CHP () -- | A process that waits for an input, then sends it out on all its -- output channels (in order) during an extended rendezvous. This is -- often used to send the output on to both the normal recipient (without -- introducing buffering) and also to a listener process that wants to -- examine the value. If the listener process is first in the list, and -- does not take the input immediately, the value will not be sent to the -- other recipients until it does. The name of the process derives from -- the notion of a wire-tap, since the listener is hidden from the other -- processes (it does not visibly change the semantics for them). tap :: Chanin a -> [Chanout a] -> CHP () -- | Sends out a single value first (the prefix) then behaves like id. prefix :: a -> Chanin a -> Chanout a -> CHP () -- | Forever reads in a value, and then sends out its successor (using -- succ). succ :: (Enum a) => Chanin a -> Chanout a -> CHP () -- | Reads in a value, and sends it out in parallel on all the given output -- channels. parDelta :: Chanin a -> [Chanout a] -> CHP () -- | Forever reads in a value, transforms it using the given function, and -- sends it out again. Note that the transformation is not applied -- strictly, so don't assume that this process will actually perform the -- computation. map :: (a -> b) -> Chanin a -> Chanout b -> CHP () -- | Forever reads in a value, and then based on applying the given -- function either discards it (if the function returns false) or sends -- it on (if the function returns True). filter :: (a -> Bool) -> Chanin a -> Chanout a -> CHP () -- | Streams all items in a Traversable container out in the order given by -- mapM on the output channel (one at a time). Lists, Maybe, and -- Set are all instances of Traversable, so this can be used for all of -- those. stream :: (Traversable t) => Chanin (t a) -> Chanout a -> CHP () -- | Forever waits for input from one of its many channels and sends it out -- again on the output channel. merger :: [Chanin a] -> Chanout a -> CHP () -- | Sends out the specified value on the given channel the specified -- number of times, then finishes. replicate :: Int -> a -> Chanout a -> CHP () -- | Forever sends out the same value on the given channel, until poisoned. -- Similar to the white-hole processes in some other frameworks. repeat :: a -> Chanout a -> CHP () -- | Forever reads values from the channel and discards them, until -- poisoned. Similar to the black-hole processes in some other -- frameworks. consume :: Chanin a -> CHP () -- | Forever reads a value from both its input channels in parallel, then -- joins the two values using the given function and sends them out -- again. For example, join (,) c d will pair the values read -- from c and d and send out the pair on the output -- channel, whereas join (&&) will send out the -- conjunction of two boolean values, join (==) will read two -- values and output whether they are equal or not, etc. join :: (a -> b -> c) -> Chanin a -> Chanin b -> Chanout c -> CHP () -- | A sorter process. When it receives its first Just x data -- item, it keeps it. When it receieves a second, it keeps the lowest of -- the two, and sends out the other one. When it receives Nothing, it -- sends out its data value, then sends Nothing too. The overall effect -- when chaining these things together is a sorting pump. You inject all -- the values with Just, then send in a single Nothing to get the results -- out (in reverse order). sorter :: (Ord a) => Chanin (Maybe a) -> Chanout (Maybe a) -> CHP () -- | Like sorter, but with a custom comparison method. You should pass in -- the equivalent of less-than: (<). sorter' :: (a -> a -> Bool) -> Chanin (Maybe a) -> Chanout (Maybe a) -> CHP () -- | Various processes that act like buffers. Poisoning either end of a -- buffer process is immediately passed on to the other side, in contrast -- to C++CSP2 and JCSP. module Control.Concurrent.CHP.Buffers -- | Acts like a limited capacity FIFO buffer of the given size. When it is -- full it accepts no input, and when it is empty it offers no output. fifoBuffer :: Int -> Chanin a -> Chanout a -> CHP () -- | Acts like a FIFO buffer with unlimited capacity. Use with caution; -- make sure you do not let the buffer grow so large that it eats up all -- your memory. When it is empty, it offers no output. It always accepts -- input. infiniteBuffer :: Chanin a -> Chanout a -> CHP () -- | Acts like a FIFO buffer of limited capacity, except that when it is -- full, it always accepts input and discards it. When it is empty, it -- does not offer output. overflowingBuffer :: Int -> Chanin a -> Chanout a -> CHP () -- | Acts like a FIFO buffer of limited capacity, except that when it is -- full, it always accepts input and pushes out the oldest item in the -- buffer. When it is empty, it does not offer output. overwritingBuffer :: Int -> Chanin a -> Chanout a -> CHP () -- | Contains a process for easily using stdin, stdout and stderr as -- channels. module Control.Concurrent.CHP.Console -- | A set of channels to be given to the process to run, containing -- channels for stdin, stdout and stderr. data ConsoleChans ConsoleChans :: Chanin Char -> Chanout Char -> Chanout Char -> ConsoleChans cStdin :: ConsoleChans -> Chanin Char cStdout :: ConsoleChans -> Chanout Char cStderr :: ConsoleChans -> Chanout Char -- | A function for running the given CHP process that wants console -- channels. When your program finishes, the console channels are -- automatically poisoned, but it's good practice to poison them yourself -- when you finish. Only ever run one of these processes at a time, or -- undefined behaviour will result. -- -- Note: getting the input handler to terminate is surpisingly difficult -- in Haskell. Currently it seems (version 6.8.2) that GHC compiling the -- end program with -threaded requires an extra character to be input -- before the input handler notices poison, whereas GHC compiled (not -- using -threaded) and GHCi act as intended. This is not a problem with -- the whole of the library, only with this consoleProcess. Unfortunately -- this means the one or two poison examples in the tutorial may not -- function correctly. I hope to resolve this problem in the next -- version. consoleProcess :: (ConsoleChans -> CHP ()) -> CHP () -- | A collection of useful functions to use with the library. module Control.Concurrent.CHP.Utils -- | Wires given processes up in a forward cycle. That is, the first -- process writes to the second, and receives from the last. It returns -- the list of wired-up processes, which you will almost certainly want -- to run in parallel. wireCycle :: (Channel r w) => [r a -> w a -> proc] -> CHP [proc] -- | Wires the given processes up in a forward pipeline. The first process -- in the list is connected to the given reading channel-end (the first -- parameter) and the writing end of a new channel, A. The second process -- is wired up to the reading end of A, and the writing end of the next -- new channel, B. This proceeds all the way to the end of the list, -- until the final process is wired to the reading end of Z (if you have -- 27 processes in the list, and therefore 26 channels in the middle of -- them) and the second parameter. The list of wired-up processes is -- returned, which you can then run in parallel. wirePipeline :: (Channel r w) => [r a -> w a -> proc] -> r a -> w a -> CHP [proc] -- | Process composition. Given two processes, composes them into a -- pipeline, like function composition (but with an opposite ordering). -- The function is associative. Using wirePipeline will be more efficient -- than foldl1 (|->|) for more than two processes. (|->|) :: (Channel r w) => (a -> w b -> CHP ()) -> (r b -> c -> CHP ()) -> (a -> c -> CHP ()) -- | The reversed version of the other operator. (|<-|) :: (Channel r w) => (r b -> c -> CHP ()) -> (a -> w b -> CHP ()) -> (a -> c -> CHP ())