-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An implementation of concurrency ideas from Communicating Sequential Processes -- -- The Communicating Haskell Processes (CHP) library is an implementation -- of message-passing concurrency ideas from Hoare's Communicating -- Sequential Processes. More details and a tutorial can be found at its -- homepage: http://www.cs.kent.ac.uk/projects/ofa/chp/, and there -- is also now a blog with examples of using the library: -- http://chplib.wordpress.com/. The library requires at least GHC -- 6.8.1. @package chp @version 1.7.1 -- | This module contains a proliferation of channel creation methods. -- -- For most uses, newChannel is the only method needed from this -- module. This creates a channel for you to use. The channel will be -- automatically destroyed during garbage collection when it falls out of -- use, so there is no need to do anything to destroy it. -- -- It is often possible for the type system to infer which channel you -- want when you use newChannel. If the types of the ends are -- known by the type system, the channel-type can be inferred. So you can -- usually just write newChannel, and depending on how you use the -- channel, the type system will figure out which one you needed. -- -- If this gives a type error along the lines of: -- --
-- Ambiguous type variables `r', `w' in the constraint: -- `Channel r w' arising from a use of `newChannel' at tmp.hs:3:24-33 -- Probable fix: add a type signature that fixes these type variable(s) ---- -- Then you must either explicitly type the channel ends you are using, -- or more simply, use one of the synonyms in -- Control.Concurrent.CHP.Channels.Synonyms to indicate which kind -- of channel you are allocating. -- -- Several other functions in this module, such as newChannelWR, -- newChannels and newChannelList are helpers built with -- newChannel to ease dealing with channel creation. -- -- The remainder of the functions in this module are related to traces -- (see Control.Concurrent.CHP.Traces), and allowing the channels -- to show up usefully in traces: see newChannel' and -- ChanOpts. -- -- The channel creation methods were refactored in version 1.5.0. Your -- code will only be affected if you were using the trace-related methods -- (for labelling the channels in traces). Instead of using -- oneToOneChannelWithLabel foo, you should use -- oneToOneChannel' $ chanLabel foo. module Control.Concurrent.CHP.Channels.Creation -- | A channel type, that can be used to get the ends of the channel via -- reader and writer data Chan r w a -- | A class used for allocating new channels, and getting the reading and -- writing ends. There is a bijective assocation between the channel, and -- its pair of end types. You can see the types in the list of instances -- below. Thus, newChannel may be used, and the compiler will -- infer which type of channel is required based on what end-types you -- get from reader and writer. Alternatively, if you -- explicitly type the return of newChannel, it will be definite -- which ends you will use. If you do want to fix the type of the channel -- you are using when you allocate it, consider using one of the many -- oneToOneChannel-like shorthand functions that fix the type. class Channel r w newChannel' :: (Channel r w, MonadCHP m) => ChanOpts a -> m (Chan r w a) sameChannel :: (Channel r w) => r a -> w a -> Bool -- | Allocates a new channel. Nothing need be done to destroy/de-allocate -- the channel when it is no longer in use. -- -- This function does not add any information to the traces: see -- newChannel' for that purpose. -- -- In version 1.5.0, this function was moved out of the Channel -- class, but that should only matter if you were declaring your own -- instances of that class (very unlikely). newChannel :: (MonadCHP m, Channel r w) => m (Chan r w a) -- | Options for channel creation; a function to show the inner data, and -- an optional label (both only affect tracing). These options can be -- passed to newChannel'. -- -- Added in version 1.5.0. data ChanOpts a ChanOpts :: (a -> String) -> Maybe String -> ChanOpts a chanOptsShow :: ChanOpts a -> a -> String chanOptsLabel :: ChanOpts a -> Maybe String -- | The default: don't show anything, don't label anything -- -- Added in version 1.5.0. defaultChanOpts :: ChanOpts a -- | Uses the Show instance for showing the data in traces, and the given -- label. -- -- Added in version 1.5.0. chanLabel :: (Show a) => String -> ChanOpts a -- | A helper that is like newChannel but returns the writing and -- reading end of the channels directly. newChannelWR :: (Channel r w, MonadCHP m) => m (w a, r a) -- | A helper that is like newChannel but returns the reading and -- writing end of the channels directly. newChannelRW :: (Channel r w, MonadCHP m) => m (r a, w a) -- | A helper class for easily creating several channels of the same type. -- The same type refers not only to what type the channel carries, but -- also to the type of channel (one-to-one no poison, one-to-any with -- poison, etc). You can write code like this: -- --
-- (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] -- | Labels a channel in the traces. It is easiest to do this at creation. -- The effect of re-labelling channels after their first use is -- undefined. -- -- Added in version 1.5.0. labelChannel :: (MonadCHP m) => Chan r w a -> String -> m () instance Channel (Shared Chanin) (Shared Chanout) instance Channel Chanin (Shared Chanout) instance Channel (Shared Chanin) Chanout instance Channel Chanin Chanout 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) module Control.Concurrent.CHP.Traces.TraceOff -- | A trace type that does not record anything. data TraceOff a -- | A type-constrained version of runCHPAndTrace. This is -- semantically identical to runCHP, but this function is useful -- with the qcCHP' function in the testing module. runCHP_TraceOff :: CHP a -> IO (Maybe a, TraceOff Unique) instance Trace TraceOff instance Show (TraceOff a) -- | 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 u CSPTrace :: (ChannelLabels u, [RecordedEvent u]) -> CSPTrace u -- | A helper function for pulling out the interesting bit from a CSP trace -- processed by labelAll. -- -- Added in version 1.5.0. getCSPPlain :: CSPTrace String -> [RecordedEvent String] runCHP_CSPTrace :: CHP a -> IO (Maybe a, CSPTrace Unique) runCHP_CSPTraceAndPrint :: CHP a -> IO () instance Trace CSPTrace instance (Ord u) => Show (CSPTrace u) -- | 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 u StructuralTrace :: (ChannelLabels u, Maybe (EventHierarchy (RecordedIndivEvent u))) -> StructuralTrace u -- | A data type representing a hierarchy of events. The count on the -- StructuralSequence count is a replicator count for that list of -- sequential items. -- -- The Show, Read, Foldable and Traversable instances were added in -- version 1.3.0. -- -- The Eq instance was added in version 1.5.0. data EventHierarchy a SingleEvent :: a -> EventHierarchy a StructuralSequence :: Int -> [EventHierarchy a] -> EventHierarchy a StructuralParallel :: [EventHierarchy a] -> EventHierarchy a -- | A helper function for pulling out the interesting bit from a -- Structural trace processed by labelAll. -- -- Added in version 1.5.0. getStructuralPlain :: StructuralTrace String -> Maybe (EventHierarchy (RecordedIndivEvent String)) runCHP_StructuralTrace :: CHP a -> IO (Maybe a, StructuralTrace Unique) 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 (Show a) => Show (EventHierarchy a) instance (Read a) => Read (EventHierarchy a) instance Trace StructuralTrace instance (Ord u) => Show (StructuralTrace u) instance Traversable EventHierarchy instance Foldable EventHierarchy instance Functor EventHierarchy instance (Eq a) => Eq (EventHierarchy a) -- | 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). -- -- The type became parameterised in version 1.3.0 newtype VCRTrace u VCRTrace :: (ChannelLabels u, [Set (RecordedEvent u)]) -> VCRTrace u -- | A helper function for pulling out the interesting bit from a VCR trace -- processed by labelAll. -- -- Added in version 1.5.0. getVCRPlain :: VCRTrace String -> [Set (RecordedEvent String)] runCHP_VCRTrace :: CHP a -> IO (Maybe a, VCRTrace Unique) runCHP_VCRTraceAndPrint :: CHP a -> IO () instance Trace VCRTrace instance (Ord u) => Show (VCRTrace u) -- | 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). -- -- For more details on the theory behind the tracing, the logic behind -- its implementation, and example traces, see the paper "Representation -- and Implementation of CSP and VCR Traces", N.C.C. Brown and M.L. -- Smith, CPA 2008. An online version can be found at: -- http://twistedsquare.com/Traces.pdf 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. -- -- The type became parameterised in version 1.3.0. type RecordedEvent u = (RecordedEventType, u) type ChannelLabels u = Map u String -- | The type of an event in the CSP and VCR traces. -- -- ClockSync was added in version 1.2.0. -- -- The extra parameter on ChannelComm and BarrierSync (which are the -- result of showing the value sent and phase ended respectively) was -- added in version 1.5.0. data RecordedEventType ChannelComm :: String -> RecordedEventType BarrierSync :: String -> RecordedEventType ClockSync :: String -> RecordedEventType -- | 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. -- -- The type u item is the unique identifier of the -- channelbarrierclock, and the Integer is a sequence identifier -- for that channelbarrierclock (first sync is 0, second sync is -- 1, etc), and finally the String shows the -- value-sentphase-endedtime involved. -- -- ClockSyncIndiv was added in version 1.2.0. -- -- The type became parameterised, and the Show and Read instances were -- added in version 1.3.0. -- -- The String parameters on ChannelWrite, ChannelRead and -- BarrierSyncIndiv were added in version 1.5.0. data RecordedIndivEvent u ChannelWrite :: u -> Integer -> String -> RecordedIndivEvent u ChannelRead :: u -> Integer -> String -> RecordedIndivEvent u BarrierSyncIndiv :: u -> Integer -> String -> RecordedIndivEvent u ClockSyncIndiv :: u -> Integer -> String -> RecordedIndivEvent u -- | Added in version 1.3.0. recordedIndivEventLabel :: RecordedIndivEvent u -> u -- | Added in version 1.3.0. recordedIndivEventSeq :: RecordedIndivEvent u -> Integer -- | 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. -- -- The trace type involved became parameterised in version 1.3.0. class Trace t runCHPAndTrace :: (Trace t) => CHP a -> IO (Maybe a, t Unique) emptyTrace :: (Trace t) => t u prettyPrint :: (Trace t, Ord u) => t u -> Doc labelAll :: (Trace t, Ord u) => t u -> t String -- | Takes a VCR trace and forms all the possible CSP traces (without -- duplicates) that could have arisen from the same execution. -- -- This is done by taking all permutations of each set in the VCR trace -- (which is a list of sets) and concatenating them with the results of -- the same process on the rest of the trace. Thus the maximum size of -- the returned set of CSP traces is the product of the sizes of all the -- non-empty sets in the VCR trace. -- -- This function was added in version 1.5.0. vcrToCSP :: (Eq u) => VCRTrace u -> [CSPTrace u] -- | Takes a structural trace and forms all the possible CSP traces -- (without duplicates) that could have arisen from the same execution. -- -- This is done -- roughly speaking -- by replaying the structural trace -- in all possible execution orderings and pulling out the CSP trace for -- each ordering. -- -- It should be the case for all structural traces t that do not -- use conjunction (every and '(<&>)'): -- --
-- structuralToCSP t =~= (concatMap vcrToCSP . structuralToVCR) t -- where a =~= b = or [a' == b' | a' <- permutations a, b' <- permutations b] ---- -- This function was added in version 1.5.0. structuralToCSP :: (Ord u) => StructuralTrace u -> [CSPTrace u] -- | Takes a structural trace and forms all the possible VCR traces -- (without duplicates) that could have arisen from the same execution. -- -- This is done -- roughly speaking -- by replaying the structural trace -- in all possible execution orderings and pulling out the VCR trace for -- each ordering. -- -- This function was added in version 1.5.0. structuralToVCR :: (Ord u) => StructuralTrace u -> [VCRTrace u] instance Monoid (Cont u) 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. -- -- In version 1.5.0, a bug was introduced such that runParallel -- [] would deadlock; this was fixed in version 1.5.1. 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 -- (). -- -- In version 1.5.0, a bug was introduced such that runParallel_ -- [] would deadlock; this was fixed in version 1.5.1. 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) -- | An operator similar to <||> that discards the output -- (more like an operator version of runParallel_). -- -- Added in version 1.1.0. (<|*|>) :: CHP a -> CHP b -> CHP () -- | A shorthand for applying mapM in parallel; really the composition of -- runParallel and map. -- -- Added in version 1.5.0. runParMapM :: (a -> CHP b) -> [a] -> CHP [b] -- | A shorthand for applying mapM_ in parallel; really the composition of -- runParallel_ and map. -- -- Added in version 1.5.0. runParMapM_ :: (a -> CHP b) -> [a] -> CHP () -- | 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 -- Control.Concurrent.CHP.Monad.runCHP and -- Control.Concurrent.CHP.Monad.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 embedding of the CHP monad back into the IO monad. The argument -- that this function takes is a CHP action (with arbitrary behaviour). -- The function is monadic, and returns something of type: IO a. This is -- an IO action that you can now use in the IO monad wherever you like. -- What it returns is the result of your original action. -- -- This function is intended for use wherever you need to supply an IO -- callback (for example with the OpenGL libraries) that needs to perform -- CHP communications. It is the safe way to do things, rather than using -- runCHP twice (and also works with CSP and VCR traces -- but not -- structural traces!). embedCHP :: CHP a -> CHP (IO (Maybe a)) -- | A convenient version of embedCHP that ignores the result embedCHP_ :: CHP a -> CHP (IO ()) -- | A helper like embedCHP for callbacks that take an argument embedCHP1 :: (a -> CHP b) -> CHP (a -> IO (Maybe b)) -- | A convenient version of embedCHP1 that ignores the result embedCHP1_ :: (a -> CHP b) -> 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) `onPoisonTrap` (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 () checkForPoison :: (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 Control.Concurrent.CHP.Alt.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. -- -- The type of this function was generalised in CHP 1.6.0. stop :: CHP a -- | 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 Control.Concurrent.CHP.Alt.alt, but -- note that waitFor 0 is not the same as skip. -- waitFor 0 Control.Concurrent.CHP.Alt.</> x will -- not always select the first guard, depending on x. Included in this is -- the lack of guarantee that waitFor 0 -- Control.Concurrent.CHP.Alt.</> 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 -- Control.Concurrent.CHP.Alt.alt anyway. -- -- NOTE: If you wish to use this as part of a choice, you must use -- -threaded as a GHC compilation option (at least under 6.8.2). waitFor :: Int -> CHP () 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 -- | Just like enroll, but starts with an already enrolled item. During the -- body, you are enrolled twice -- once from the original enrolled item -- (which is still valid in the body) and once from the new enrolled item -- passed to the inner function. Afterwards, you are enrolled once, on -- the original item you had. -- -- This function was added in version 1.3.2. furtherEnroll :: (Enrollable b z) => Enrolled 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 -- | Given a command to allocate a new barrier, and a list of processes -- that use that barrier, enrolls the appropriate number of times (i.e. -- the list length) and runs all the processes in parallel using that -- barrier, then returns a list of the results. -- -- If you have already allocated the barrier, pass return bar as -- the first parameter. -- -- Added in version 1.7.0. enrollAll :: (Enrollable b p) => CHP (b p) -> [Enrolled b p -> CHP a] -> CHP [a] -- | Like enrollAll, but discards the results. -- -- Added in version 1.7.0. enrollAll_ :: (Enrollable b p) => CHP (b p) -> [Enrolled b p -> CHP a] -> CHP () -- | A module containing the ALT constructs. An ALT (a term inherited from -- occam) is a choice between several alternate 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 ---- -- Note that if you wait for a sequential composition: -- --
-- (readChannel c >>= writeChannel d) <-> (writeChannel e 6 >> readChannel f) ---- -- This only waits for the first action in both (reading from channel c, -- or writing to channel e), not for all of the actions (as, for example, -- an STM transaction would). 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 actions -- 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 descending priority. The first -- available action is chosen (biased towards actions nearest the -- beginning of the list), its body run, and its value returned. -- -- What priority means here is a difficult thing, and in some ways a -- historical artifact. We can group the guards into three categories: -- --
-- priAlt [ skip, x ] ---- -- Will always select the first guard, whereas: -- --
-- priAlt [ x , skip ] ---- -- Is an effective way to poll and see if x is ready, otherwise the -- skip will be chosen. However, there is no priority between -- synchronisation guards and time-out guards. So the two lines: -- --
-- priAlt [ x, y ] -- priAlt [ y, x ] ---- -- May have the same or different behaviour (when x and y are not dummy -- guards), there is no guarantee either way. The reason behind this is -- that if you ask for: -- --
-- priAlt [ readChannel c, writeChannel d 6 ] ---- -- And the process at the other end is asking for: -- --
-- priAlt [ readChannel d, writeChannel c 8 ] ---- -- Whichever channel is chosen by both processes will not satisfy the -- priority at one end (if such priority between channels was supported). 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 -- | Runs all the given processes in parallel with each other, but only -- when the choice at the beginning of each item is ready. -- -- So for example, if you do: -- --
-- every [ readChannel c >>= writeChannel d, readChannel e >>= writeChannel f] ---- -- This will forward values from c and e to d and f respectively in -- parallel, but only once both channels c and e are ready to be read -- from. So f cannot be written to before c is read from (contrast this -- with what would happen if every were replaced with -- runParallel). -- -- This behaviour can be somewhat useful, but every is much more -- powerful when used as part of an alt. This code: -- --
-- alt [ every [ readChannel c, readChannel d] -- , every [ writeChannel e 6, writeChannel f 8] ] ---- -- Waits to either read from channels c and d, or to write to channels e -- and f. -- -- The events involved can partially overlap, e.g. -- --
-- alt [ every [ readChannel a, readChannel b] -- , every [ readChannel a, writeChannel c 6] ] ---- -- This will wait to either read from channels a and b, or to read from a -- and write to c, whichever combination is ready first. If both are -- ready, the choice between them will be arbitrary (just as with any -- other choices; see alt for more details). -- -- The sets can even be subsets of each other, such as: -- --
-- alt [ every [ readChannel a, readChannel b] -- , every [ readChannel a, readChannel b, readChannel b] ] ---- -- In this case there are no guarantees as to which choice will happen. -- Do not assume it will be the smaller, and do not assume it will be the -- larger. -- -- Be wary of what happens if a single event is included multiple times -- in the same every, as this may not do what you expect (with or -- without choice). Consider: -- --
-- every [ readChannel c >> writeChannel d 6 -- , readChannel c >> writeChannel d 8 ] ---- -- What will happen is that the excecution will wait to read from c, but -- then it will execute only one of the bodies (an arbitrary choice). In -- general, do not rely on this behaviour, and instead try to avoid -- having the same events in an every. Also note that if you -- synchronise on a barrier twice in an every, this will only -- count as one member enrolling, even if you have two enrolled ends! For -- such a use, look at runParallel instead. -- -- Also note that this currently applies to both ends of channels, so -- that: -- --
-- every [ readChannel c, writeChannel c 2 ] ---- -- Will block indefinitely, rather than completing the communication. -- -- Each item every must support choice (and in fact only a subset -- of the items supported by alt are supported by every). -- Currently the items in the list passed to every must be one of -- the following: -- --
-- every [ readChannel c -- , alt [ readChannel d, readChannel e ] ] ---- -- To wait for c and (d or e) like this you must expand it out into (c -- and d) or (c and e): -- --
-- alt [ every [ readChannel c, readChannel d] -- , every [ readChannel c, readChannel e] ] ---- -- As long as x meets the conditions laid out above, every [x] -- will have the same behaviour as x. -- -- Added in version 1.1.0 every :: [CHP a] -> CHP [a] -- | A useful operator that acts like every. The operator is -- associative and commutative (see every for notes on -- idempotence). When you have lots of things to join with this operator, -- it's probably easier to use the every function. -- -- Added in version 1.1.0 (<&>) :: CHP a -> CHP b -> CHP (a, b) -- | 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 () -- | A useful type synonym for enrolled barriers with no phases -- -- Added in 1.1.0 type EnrolledBarrier = Enrolled 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. See newBarrier. 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: -- --
-- Ambiguous type variables `r', `w' in the constraint: -- `Channel r w' arising from a use of `newChannel' at tmp.hs:3:24-33 -- Probable fix: add a type signature that fixes these type variable(s) ---- -- Then you may want to substitute your use of newChannel for -- oneToOneChannel (if you are not using channel sharing). module Control.Concurrent.CHP.Channels.Synonyms type OneToOneChannel = Chan Chanin Chanout -- | A type-constrained version of newChannel. oneToOneChannel :: (MonadCHP m) => m (OneToOneChannel a) -- | A type-constrained version of newChannel'. -- -- Added in version 1.5.0. oneToOneChannel' :: (MonadCHP m) => ChanOpts a -> m (OneToOneChannel a) type OneToAnyChannel = Chan (Shared Chanin) (Chanout) -- | A type-constrained version of newChannel. oneToAnyChannel :: (MonadCHP m) => m (OneToAnyChannel a) -- | A type-constrained version of newChannel'. -- -- Added in version 1.5.0. oneToAnyChannel' :: (MonadCHP m) => ChanOpts a -> m (OneToAnyChannel a) type AnyToOneChannel = Chan (Chanin) (Shared Chanout) -- | A type-constrained version of newChannel. anyToOneChannel :: (MonadCHP m) => m (AnyToOneChannel a) -- | A type-constrained version of newChannel'. -- -- Added in version 1.5.0. anyToOneChannel' :: (MonadCHP m) => ChanOpts a -> m (AnyToOneChannel a) type AnyToAnyChannel = Chan (Shared Chanin) (Shared Chanout) -- | A type-constrained version of newChannel. anyToAnyChannel :: (MonadCHP m) => m (AnyToAnyChannel a) -- | A type-constrained version of newChannel'. -- -- Added in version 1.5.0. anyToAnyChannel' :: (MonadCHP m) => ChanOpts a -> m (AnyToAnyChannel a) -- | All the channel-ends in CHP are instances of ReadableChannel -- (for ends that you can read from) or WriteableChannel (for ends -- that you can write to). -- -- The readChannel and writeChannel functions are the -- standard way to communicate on a channel. These functions wait for the -- other party in the communication to arrive, then exchange the data, -- then complete. In pseudo-code, the semantics are like this when two -- parties (shown here as two columns) communicate: -- --
-- do sync sync -- x <- return y -- done done ---- -- Further options are offered by the extReadChannel and -- extWriteChannel channels, which allow either side to perform -- additional (so-called extended) actions during the communication. The -- semantics when both sides are performing extended actions are: -- --
-- do sync sync -- y <- extWriteAction -- x <- return y -- x' <- extReadAction x done -- done done -- return x' ---- -- Neither end need know that the other is performing an extended action, -- and any combination is possible (e.g. a normal writeChannel -- with an extReadChannel). module Control.Concurrent.CHP.Channels.Communication -- | 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 () extWriteChannel' :: (WriteableChannel chanEnd) => chanEnd a -> CHP (a, b) -> CHP b -- | A useful synonym for flip writeChannel. Especially useful -- with claim so that instead of writing claim output (flip -- writeChannel 6) you can write claim output (writeValue -- 6). -- -- Added in version 1.5.0. writeValue :: (WriteableChannel chanEnd) => a -> chanEnd a -> CHP () -- | A helper function that uses the parallel strategies library (see the -- paper: "Algorithm + Strategy = Parallelism", P.W. Trinder et al, JFP -- 8(1) 1998, -- http://www.macs.hw.ac.uk/~dsg/gph/papers/html/Strategies/strategies.html) -- to make sure that the value sent down a channel is strictly evaluated -- by the sender before transmission. -- -- This is useful when you want to write worker processes that evaluate -- data and send it back to some "harvester" process. By default the -- values sent back may be unevaluated, and thus the harvester might end -- up doing the evaluation. If you use this function, the value is -- guaranteed to be completely evaluated before sending. -- -- Added in version 1.0.2. writeChannelStrict :: (NFData a, WriteableChannel chanEnd) => chanEnd a -> a -> CHP () instance WriteableChannel Chanout instance ReadableChannel Chanin -- | The module containing all the different types of channels in CHP. -- -- A communication in CHP is always synchronised: the writer must wait -- until the reader arrives to take the data. There is thus no automatic -- or underlying buffering of data. (If you want to use buffers, see the -- Control.Concurrent.CHP.Buffers module). -- -- If it helps, a channel communication can be thought of as a -- distributed binding. Imagine you have a process that creates a channel -- and then becomes the parallel composition of two sub-processes that at -- some point communicate on that channel (formatted here as two columns -- for illustration): -- --
-- do c <- oneToOneChannel -- (<||>) -- do p do p' -- q y <- q' -- x <- readChannel (reader c) writeChannel (writer c) y -- r r' -- s x s' ---- -- It is as if, at the point where the two processes want to communicate, -- they come together and directly bind the value from one process in the -- other: -- --
-- do c <- oneToOneChannel -- (<||>) -- do p do p' -- q y <- q' -- x <- return y -- r r' -- s x s' ---- -- The Control.Concurrent.CHP.Channels.Creation contains functions -- relating to the creation of channels. Channels are used via their ends -- -- see the Control.Concurrent.CHP.Channels.Ends module, and the -- Control.Concurrent.CHP.Channels.Communication module. -- -- Broadcast and reduce channels are available in the -- Control.Concurrent.CHP.Channels.BroadcastReduce module, which -- is not automatically re-exported here. -- -- This module was split into several smaller modules in version 1.5.0. -- Since it re-exports all the new modules, your code should not be -- affected at all. module Control.Concurrent.CHP.Channels -- | 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 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 was undefined in versions 1.5.1 and earlier (the writer may -- block, or may communicate happily to no-one). However, in version -- 1.6.0 onwards the semantics are clear: a writer that communicates on a -- broadcast channel with no readers involved will succeed immediately, -- communicating to no-one (i.e. the data is lost). Similarly, a read -- from a reduce channel will immediately return mempty when no -- writers are enrolled. It is possible to get into a state of livelock -- (by repeatedly performing these instant communications) in these sorts -- of situations. -- -- This module also contains reduce channels (added in version 1.1.1). -- Because in CHP channels must have the same type at both ends, we use -- the Monoid type-class. It is important to be aware that the order of -- mappends will be non-deterministic, and thus you should either use an -- mappend that is commutative or code around this restruction. -- -- For example, a common thing to do would be to use lists as the type -- for reduce channels, make each writer write a single item list (but -- more is possible), then use the list afterwards, but be aware that it -- is unordered. If it is important to have an ordered list, make each -- writer write a pair containing a (unique) index value and the real -- data, then sort by the index value and discard it. -- -- Since reduce channels were added after the initial library design, -- there is a slight complication: it is not possible to use newChannel -- (and all similar functions) with reduce channels because it is -- impossible to express the Monoid constraint for the Channel instance. -- Instead, you must use manyToOneChannel and manyToAnyChannel. module Control.Concurrent.CHP.Channels.BroadcastReduce -- | The reading end of a broadcast channel. You must enroll on it before -- you can read from it or poison it. -- -- The Eq instance was added in version 1.4.0. data BroadcastChanin a -- | The writing end of a broadcast channel. -- -- The Eq instance was added in version 1.4.0. data BroadcastChanout a type OneToManyChannel = Chan BroadcastChanin BroadcastChanout type AnyToManyChannel = Chan BroadcastChanin (Shared BroadcastChanout) oneToManyChannel :: (MonadCHP m) => m (OneToManyChannel a) anyToManyChannel :: (MonadCHP m) => m (AnyToManyChannel a) -- | Added in version 1.5.0. -- -- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not -- appear correctly in the traces. oneToManyChannel' :: (MonadCHP m) => ChanOpts a -> m (OneToManyChannel a) -- | Added in version 1.5.0. -- -- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not -- appear correctly in the traces. anyToManyChannel' :: (MonadCHP m) => ChanOpts a -> m (AnyToManyChannel a) -- | The reading end of a reduce channel. -- -- The Eq instance was added in version 1.4.0. data ReduceChanin a -- | The writing end of a reduce channel. You must enroll on it before you -- can read from it or poison it. -- -- The Eq instance was added in version 1.4.0. data ReduceChanout a -- | The reduce channel version of sameChannel. -- -- This function was added in version 1.4.0. sameReduceChannel :: ReduceChanin a -> ReduceChanout a -> Bool type ManyToOneChannel = Chan ReduceChanin ReduceChanout type ManyToAnyChannel = Chan (Shared ReduceChanin) ReduceChanout manyToOneChannel :: (Monoid a, MonadCHP m) => m (ManyToOneChannel a) manyToAnyChannel :: (Monoid a, MonadCHP m) => m (ManyToAnyChannel a) -- | Added in version 1.5.0. -- -- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not -- appear correctly in the traces. manyToOneChannel' :: (Monoid a, MonadCHP m) => ChanOpts a -> m (ManyToOneChannel a) -- | Added in version 1.5.0. -- -- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not -- appear correctly in the traces. manyToAnyChannel' :: (Monoid a, MonadCHP m) => ChanOpts a -> m (ManyToAnyChannel a) instance Eq (ReduceChanout a) instance Eq (ReduceChanin a) instance Eq (BroadcastChanout a) instance Eq (BroadcastChanin a) instance Poisonable (ReduceChanin a) instance Poisonable (Enrolled ReduceChanout a) instance ReadableChannel ReduceChanin instance WriteableChannel (Enrolled ReduceChanout) instance Enrollable ReduceChanout a instance Eq (ReduceChannel a) 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 instance Eq (BroadcastChannel a) -- | Since version 1.5.0, this module is a synonym for the -- Control.Concurrent.CHP.Channels.BroadcastReduce module, and is -- liable to be removed in a future version. module Control.Concurrent.CHP.BroadcastChannels -- | 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 :: (ReadableChannel r, Poisonable (r a), WriteableChannel w, Poisonable (w a)) => r a -> w a -> CHP () -- | Forever forwards the value onwards. This is like id but does -- not add any buffering to your network, and its presence is -- indetectable to the process either side. -- -- extId is a unit of the associative operator -- Control.Concurrent.CHP.Utils.|->|. -- -- The behaviour of this process was corrected in version 1.1.0 to work -- properly when the reader of its output channel was offering choice. 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 -- except -- when the readers of the channels are offering a choice). 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 () -- | Discards the first value it receives then act likes id. -- -- Added in version 1.5.0. tail :: 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. If you require a strict transformation, use map'. map :: (a -> b) -> Chanin a -> Chanout b -> CHP () -- | Like map, but applies the transformation strictly before -- sending on the value. -- -- Added in version 1.1.0. map' :: (NFData b) => (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 Data.Traversable.Traversable container -- out in the order given by Data.Traversable.mapM on the output -- channel (one at a time). Lists, Maybe, and -- Data.Set.Set are all instances of -- Data.Traversable.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 () -- | For the duration of the given process, acts as a consume process, but -- stops when the given process stops. Note that there could be a timing -- issue where extra inputs are consumed at the end of the lifetime of -- the process. Note also that while poison from the given process will -- be propagated on the consumption channel, there is no mechanism to -- propagate poison from the consumption channel into the given process. -- -- Added in version 1.2.0. consumeAlongside :: Chanin a -> CHP b -> CHP b -- | 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 () -- | Forever reads a value from all its input channels in parallel, then -- joins the values into a list in the same order as the channels, and -- sends them out again. joinList :: [Chanin a] -> Chanout [a] -> CHP () -- | Forever reads a pair from its input channel, then in parallel sends -- out the first and second parts of the pair on its output channels. -- -- Added in version 1.0.2. split :: Chanin (a, b) -> Chanout a -> Chanout b -> 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 () -- | A shared variable process. Given an initial value and two channels, it -- continually offers to output its current value or read in a new one. -- -- Added in version 1.1.1 -- -- Note that prior to version 1.2.0 (i.e. in version 1.1.1) there was a -- bug where poison would not be propagated between the input and output. valueStore :: (ReadableChannel r, Poisonable (r a), WriteableChannel w, Poisonable (w a)) => a -> r a -> w a -> CHP () -- | A shared variable process. The same as valueStore, but initially waits -- to read its starting value before then offering to either output its -- current value or read in a new one. -- -- Added in version 1.1.1 -- -- Note that prior to version 1.2.0 (i.e. in version 1.1.1) there was a -- bug where poison would not be propagated between the input and output. valueStore' :: (ReadableChannel r, Poisonable (r a), WriteableChannel w, Poisonable (w a)) => r a -> w a -> CHP () -- | Continually waits for a specific time on the given clock, each time -- applying the function to work out the next specific time to wait for. -- The most common thing to pass is Prelude.succ or (+1). -- -- Added in version 1.2.0. advanceTime :: (Waitable c, Ord t) => (t -> t) -> Enrolled c t -> CHP () -- | A collection of useful functions to use with the library. -- -- The most useful operation is pipeline which you can use to wire -- up a list of processes into a line, and run them. The corresponding -- |->| operator is a simple binary version that can be a -- little more concise. When the pipeline has channels going in both -- directions rather than just one, dualPipeline and/or -- |<->| can be used. Several other variants on these -- functions are also provided, including operators to use at the -- beginning and ends of pipelines. -- -- Most of the functions in this module are superseded or generalised by -- those in the Control.Concurrent.CHP.Connect module since -- version 1.7.0, so you should look to use those connectors rather than -- these, as the connects in this module may be removed at some point in -- the future. 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] -- | Like wireCycle, but works with processes that connect with a channel -- in both directions. -- -- This function was added in version 1.4.0. wireDualCycle :: (Channel r w, Channel r' w') => [(r a, w' b) -> (r' b, 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] -- | Like wirePipeline, but works with processes that connect with a -- channel in both directions. -- -- This function was added in version 1.4.0. wireDualPipeline :: (Channel r w, Channel r' w') => [(r a, w' b) -> (r' b, w a) -> proc] -> (r a, w' b) -> (r' b, w a) -> CHP [proc] -- | A specialised version of wirePipeline. Given a list of -- processes, composes them into an ordered pipeline, that takes the -- channel-ends for the sticking out ends of the pipeline and gives a -- process that returns a list of their results. This is equivalent to -- wirePipeline, with the return value fed to runParallel. -- -- Added in version 1.0.2. pipeline :: [Chanin a -> Chanout a -> CHP b] -> Chanin a -> Chanout a -> CHP [b] -- | Like pipeline, but works with processes that connect with a channel in -- both directions. -- -- This function was added in version 1.4.0. dualPipeline :: [(Chanin a, Chanout b) -> (Chanin b, Chanout a) -> CHP c] -> (Chanin a, Chanout b) -> (Chanin b, Chanout a) -> CHP [c] -- | A specialised version of wireCycle. Given a list of processes, -- composes them into a cycle and runs them all in parallel. This is -- equivalent to wireCycle with the return value fed into -- runParallel. -- -- Added in version 1.0.2. cycle :: [Chanin a -> Chanout a -> CHP b] -> CHP [b] -- | Like cycle, but works with processes that connect with a channel in -- both directions. -- -- This function was added in version 1.4.0. dualCycle :: [(Chanin a, Chanout b) -> (Chanin b, Chanout a) -> CHP c] -> CHP [c] -- | 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. -- -- The type for this process became more specific in version 1.2.0. (|->|) :: (a -> Chanout b -> CHP ()) -> (Chanin b -> c -> CHP ()) -> (a -> c -> CHP ()) -- | Like (|->|), but labels the channel and uses show for the traces. -- -- Added in version 1.5.0. (|->|^) :: (Show b) => (a -> Chanout b -> CHP ()) -> (String, Chanin b -> c -> CHP ()) -> (a -> c -> CHP ()) -- | Process composition that works with processes that connect with a -- channel in both directions. Like (|->|), but connects a channel in -- each direction. -- -- This function was added in version 1.4.0. (|<->|) :: (a -> (Chanin b, Chanout c) -> CHP ()) -> ((Chanin c, Chanout b) -> d -> CHP ()) -> (a -> d -> CHP ()) -- | The reversed version of the other operator. -- -- The type for this process became more specific in version 1.2.0. (|<-|) :: (Chanin b -> c -> CHP ()) -> (a -> Chanout b -> CHP ()) -> (a -> c -> CHP ()) -- | A function to use at the start of a pipeline you are chaining together -- with the |->| operator. Added in version 1.2.0. (->|) :: (Chanout b -> CHP ()) -> (Chanin b -> c -> CHP ()) -> (c -> CHP ()) -- | A function to use at the end of a pipeline you are chaining together -- with the |->| operator. Added in version 1.2.0. (|->) :: (a -> Chanout b -> CHP ()) -> (Chanin b -> CHP ()) -> (a -> CHP ()) -- | A function to use at the start of a pipeline you are chaining together -- with the |<->| operator. Added in version 1.4.0. (|<->) :: (a -> (Chanin b, Chanout c) -> CHP ()) -> ((Chanin c, Chanout b) -> CHP ()) -> (a -> CHP ()) -- | A function to use at the end of a pipeline you are chaining together -- with the |<->| operator. Added in version 1.4.0. (<->|) :: ((Chanin b, Chanout c) -> CHP ()) -> ((Chanin c, Chanout b) -> a -> CHP ()) -> (a -> CHP ()) -- | Provides an instance of Arrow for process pipelines. As described in -- the original paper on arrows, they can be used to represent stream -- processing, so CHP seemed like a possible fit for an arrow. -- -- Whether this is actually an instance of Arrow depends on -- technicalities. This can be demonstrated with the arrow law arr id -- >>> f = f = f >>> arr id. Whether CHP satisfies -- this arrow law depends on the definition of equality. -- --
-- runPipeline (arr (*2) *** arr (+1)) ---- -- Arrows are more useful where you already have processes written that -- process data and you want to easily wire them together. The arrow -- notation is probably easier for doing that than declaring all the -- channels yourself and composing everything in parallel. runPipeline :: ProcessPipeline a b -> Chanin a -> Chanout b -> CHP () -- | Adds a wrapper that forms this process into the right data type to be -- part of an arrow. -- -- Any process you apply this to should produce exactly one output per -- input, or else you will find odd behaviour resulting (including -- deadlock). So for example, don't use arrowProcess -- (Control.Concurrent.CHP.Common.filter ...) or -- arrowProcess Control.Concurrent.CHP.Common.stream -- inside any arrow combinators other than >>> and <<<. -- -- Added in version 1.1.0 arrowProcess :: (Chanin a -> Chanout b -> CHP ()) -> ProcessPipeline a b -- | Like the arr function of the ProcessPipeline arrow instance, but fully -- evaluates the result before sending it. If you are building process -- pipelines with arrows to try and get some parallel speed-up, you -- should try this function instead of arr itself. -- -- Added in version 1.3.2 arrStrict :: (NFData b) => (a -> b) -> ProcessPipeline a b -- | ProcessPipelineLabel is a version of ProcessPipeline that -- allows the processes to be labelled, and thus in turn for the channels -- connecting the processes to be automatically labelled. -- ProcessPipelineLabel is not an instance of Arrow, but it does have a -- lot of similarly named functions for working with it. This awkwardness -- is due to the extra Show constraints on the connectors that allow the -- arrow's contents to appear in traces. -- -- If you don't use traces, use ProcessPipeline. If you do use -- traces, and want to have better labels on the process and values used -- in your arrows, consider switching to ProcessPipelineLabel. -- -- ProcessPipelineLabel and all the functions that use it, were added in -- version 1.5.0. data ProcessPipelineLabel a b -- | Like runPipeline but for ProcessPipelineLabel runPipelineLabel :: ProcessPipelineLabel a b -> Chanin a -> Chanout b -> CHP () -- | Like arrowProcess, but allows the process to be labelled. The -- same warnings as arrowProcess apply. arrowProcessLabel :: String -> (Chanin a -> Chanout b -> CHP ()) -> ProcessPipelineLabel a b -- | Like arr for ProcessPipeline, but allows the process to -- be labelled. arrLabel :: String -> (a -> b) -> ProcessPipelineLabel a b -- | Like arrStrict, but allows the process to be labelled. arrStrictLabel :: (NFData b) => String -> (a -> b) -> ProcessPipelineLabel a b -- | The '(>>>)' arrow combinator, for -- ProcessPipelineLabel. (*>>>*) :: (Show b) => ProcessPipelineLabel a b -> ProcessPipelineLabel b c -> ProcessPipelineLabel a c -- | The '(<<<)' arrow combinator, for -- ProcessPipelineLabel. (*<<<*) :: (Show b) => ProcessPipelineLabel b c -> ProcessPipelineLabel a b -> ProcessPipelineLabel a c -- | The '(&&&)' arrow combinator, for -- ProcessPipelineLabel. (*&&&*) :: (Show b, Show c, Show c') => ProcessPipelineLabel b c -> ProcessPipelineLabel b c' -> ProcessPipelineLabel b (c, c') -- | The '(***)' arrow combinator, for ProcessPipelineLabel. (*****) :: (Show b, Show b', Show c, Show c') => ProcessPipelineLabel b c -> ProcessPipelineLabel b' c' -> ProcessPipelineLabel (b, b') (c, c') instance ArrowChoice ProcessPipeline instance Arrow ProcessPipeline instance Category ProcessPipeline instance Functor (ProcessPipeline a) -- | A module containing CHP behaviours. See offer for details. -- -- This whole module was added in CHP 1.6.0. module Control.Concurrent.CHP.Behaviours -- | This data represents a behaviour (potentially repeated) that will -- result in returning a value of type a. See offer for -- more details. data CHPBehaviour a -- | Offers the given behaviour until finished. -- -- For example, -- --
-- offer $ repeatedly p `alongside` repeatedly q ---- -- will repeatedly offer p and q without ever terminating. This: -- --
-- offer $ repeatedly p `alongside` repeatedly q `alongside` endWhen r ---- -- will offer p repeatedly and q repeatedly and r, until r happens, at -- which point the behaviour will end. This: -- --
-- offer $ once p `alongside` endWhen q ---- -- will offer p and q; if p happens first it will wait for q, but if q -- happens first it will finish. This: -- --
-- offer $ once p `alongside` endWhen q `alongside` endWhen r ---- -- permits p to happen at most once, while either of q or r happening -- will finish the call. -- -- All sorts of combinations are possible, but it is important to note -- that you need at least one endWhen event if you ever intend the -- call to finish. Some laws involving offer (ignoring the types -- and return values) are: -- --
-- offer (repeatedly p) == forever p -- offer (once p) == p >> stop -- i.e. it does not finish -- offer (endWhen q) == Just <$> q -- offer (endWhen p `alongside` endWhen q) == p <-> q -- offer (once p `alongside` endWhen q) == (p >> q) <-> q ---- -- Most other uses of offer and alongside do not reduce -- down to simple CHP programs, which is of course their attraction. offer :: CHPBehaviour a -> CHP a -- | Offers all the given behaviours together, and gives back a list of the -- outcomes. -- -- This is roughly a shorthand for offer . foldl1 alongside, -- except that if you pass the empty list, you simply get the empty list -- returned (rather than an error) offerAll :: [CHPBehaviour a] -> CHP [a] -- | Offers one behaviour alongside another, combining their semantics. See -- offer. -- -- This operation is semantically associative and commutative. alongside :: CHPBehaviour a -> CHPBehaviour b -> CHPBehaviour (a, b) -- | Offers one behaviour alongside another, combining their semantics. See -- offer. Unlike alongside, discards the output of the -- behaviours. -- -- This operation is associative and commutative. alongside_ :: CHPBehaviour a -> CHPBehaviour b -> CHPBehaviour () -- | Offers the given behaviour, and when it occurs, ends the entire call -- to offer. Returns Just the result if the behaviour happens, -- otherwise gives Nothing. endWhen :: CHP a -> CHPBehaviour (Maybe a) -- | Offers the given behaviour, and when it occurs, does not offer it -- again. Returns Just the result if the behaviour happens, otherwise -- gives Nothing. once is different to endWhen because the -- latter terminates the call to offer regardless of other -- behaviours, whereas once does not terminate the call to -- offer, it just won't be offered again during the call to -- offer. Thus if you only offer some once items without -- any endWhen, then after all the once events have -- happened, the process will deadlock. once :: CHP a -> CHPBehaviour (Maybe a) -- | Repeatedly offers the given behaviour until the outer call to -- offer is terminated by an endWhen event. A list is -- returned (in chronological order) of the results of each occurrence of -- the behaviour. repeatedly :: CHP a -> CHPBehaviour [a] -- | Like repeatedly, but discards the output. Useful if the event -- is likely to occur a lot, and you don't need the results. repeatedly_ :: CHP a -> CHPBehaviour () -- | Like repeatedly, but allows some state (of type a) to -- be passed from one subsequent call to another, as well as generating -- the results of type b. To begin with the function (first -- parameter) will be called with the initial state (second parameter). -- If chosen, it will return the new state, and a result to be -- accumulated into the list. The second call to the function will be -- passed the new state, to then return the even newer state and a second -- result, and so on. -- -- If you want to use this with the StateT monad transformer from the mtl -- library, you can call: -- --
-- repeatedlyRecurse (runStateT myStateAction) initialState -- where -- myStateAction :: StateT s CHP a -- initialState :: s --repeatedlyRecurse :: (a -> CHP (b, a)) -> a -> CHPBehaviour [b] -- | Like repeatedlyRecurse, but does not accumulate a list of -- results. -- -- If you want to use this with the StateT monad transformer from the mtl -- library, you can call: -- --
-- repeatedlyRecurse (execStateT myStateAction) initialState -- where -- myStateAction :: StateT s CHP a -- initialState :: s --repeatedlyRecurse_ :: (a -> CHP a) -> a -> CHPBehaviour () instance Functor CHPBehaviour -- | 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 with unlimited capacity, but accumulates -- sequential inputs into a list which it offers in a single output. 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 the empty -- list. It always accepts input. Once it has sent out a value (or -- values) it removes them from its internal storage. -- -- Added in version 1.2.0. accumulatingInfiniteBuffer :: 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 () -- | A module of operators for connecting processes together. -- -- This whole module was added in version 1.7.0. module Control.Concurrent.CHP.Connect -- | Indicates that its two parameters can be joined together -- automatically. -- -- Rather than use connect directly, you will want to use the -- operators such as '(=)'. There are different forms of this -- operator for in the middle of a pipeline (where you still need further -- parameters to each process), and at the ends. See also -- pipelineConnect and pipelineConnectComplete. class Connectable l r connect :: (Connectable l r) => ((l, r) -> CHP ()) -> CHP () -- | Joins together the given two processes and runs them in parallel. (<=>) :: (Connectable l r) => (a -> l -> CHP ()) -> (r -> b -> CHP ()) -> a -> b -> CHP () -- | Joins together the given two processes and runs them in parallel. (|<=>) :: (Connectable l r) => (l -> CHP ()) -> (r -> b -> CHP ()) -> b -> CHP () -- | Joins together the given two processes and runs them in parallel. (<=>|) :: (Connectable l r) => (a -> l -> CHP ()) -> (r -> CHP ()) -> a -> CHP () -- | Joins together the given two processes and runs them in parallel. (|<=>|) :: (Connectable l r) => (l -> CHP ()) -> (r -> CHP ()) -> CHP () -- | Like foldl1 (=); connects a pipeline of processes -- together. If the list is empty, it returns a process that ignores both -- its arguments and returns instantly. pipelineConnect :: (Connectable l r) => [r -> l -> CHP ()] -> r -> l -> CHP () -- | Connects the given beginning process, the list of middle processes, -- and the end process into a pipeline and runs them all in parallel. If -- the list is empty, it connects the beginning directly to the end. pipelineConnectComplete :: (Connectable l r) => (l -> CHP ()) -> [r -> l -> CHP ()] -> (r -> CHP ()) -> CHP () -- | Like pipelineConnect but also connects the last process into -- the first. If the list is empty, it returns immediately. cycleConnect :: (Connectable l r) => [r -> l -> CHP ()] -> CHP () -- | Like Connectable, but allows an extra parameter. -- -- The API (and name) for this is still in flux, so do not rely on it -- just yet. class ConnectableExtra l r where { type family ConnectableParam l; } connectExtra :: (ConnectableExtra l r) => ConnectableParam l -> ((l, r) -> CHP ()) -> CHP () -- | Like '(=)' but with ConnectableExtra connectWith :: (ConnectableExtra l r) => ConnectableParam l -> (a -> l -> CHP ()) -> (r -> b -> CHP ()) -> a -> b -> CHP () instance (ConnectableExtra al ar, ConnectableExtra bl br, ConnectableExtra cl cr, ConnectableExtra dl dr, ConnectableExtra el er) => ConnectableExtra (al, bl, cl, dl, el) (ar, br, cr, dr, er) instance (Connectable al ar, Connectable bl br, Connectable cl cr, Connectable dl dr, Connectable el er) => Connectable (al, bl, cl, dl, el) (ar, br, cr, dr, er) instance (ConnectableExtra al ar, ConnectableExtra bl br, ConnectableExtra cl cr, ConnectableExtra dl dr) => ConnectableExtra (al, bl, cl, dl) (ar, br, cr, dr) instance (Connectable al ar, Connectable bl br, Connectable cl cr, Connectable dl dr) => Connectable (al, bl, cl, dl) (ar, br, cr, dr) instance (ConnectableExtra al ar, ConnectableExtra bl br, ConnectableExtra cl cr) => ConnectableExtra (al, bl, cl) (ar, br, cr) instance (Connectable al ar, Connectable bl br, Connectable cl cr) => Connectable (al, bl, cl) (ar, br, cr) instance (ConnectableExtra al ar, ConnectableExtra bl br) => ConnectableExtra (al, bl) (ar, br) instance (Connectable al ar, Connectable bl br) => Connectable (al, bl) (ar, br) instance ConnectableExtra (Enrolled PhasedBarrier ph) (Enrolled PhasedBarrier ph) instance Connectable (Enrolled PhasedBarrier ()) (Enrolled PhasedBarrier ()) instance ConnectableExtra (Chanin a) (Chanout a) instance Connectable (Chanin a) (Chanout a) instance ConnectableExtra (Chanout a) (Chanin a) instance Connectable (Chanout a) (Chanin a) -- | 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. -- -- When using this process, due to the way that the console handlers are -- terminated, you may sometimes see a notice that a thread was killed. -- This is normal behaviour (unfortunately). consoleProcess :: (ConsoleChans -> CHP ()) -> CHP () -- | A module containing some useful functions for testing CHP programs, -- both in the QuickCheck 2 framework and using HUnit. -- -- This whole module was added in version 1.4.0. module Control.Concurrent.CHP.Test -- | A wrapper around the CHP type that supports some QuickCheck -- Testable instances. See qcCHP and qcCHP'. -- -- Added in version 1.5.0. data QuickCheckCHP a -- | Turns a CHP program into a QuickCheckCHP for use with -- Testable instances. -- -- Equivalent to qcCHP' . runCHP_CSPTrace. -- -- Added in version 1.5.0. qcCHP :: CHP a -> QuickCheckCHP a -- | Takes the command that runs a CHP program and gives back a -- QuickCheckCHP item for use with Testable instances. -- -- You use this function like: -- --
-- qcCHP' (runCHP_CSPTrace p) ---- -- To test process p with a CSP trace if it fails. To turn off -- the display of tracing when a test fails, use: -- --
-- qcCHP' (runCHP_TraceOff p) ---- -- Added in version 1.5.0. qcCHP' :: (Trace t) => IO (Maybe a, t Unique) -> QuickCheckCHP a -- | Tests a process that takes a single input and produces a single -- output, using QuickCheck. -- -- The first parameter is a pure function that takes the input to the -- process, the output the process gave back, and indicates whether this -- is okay (True = test pass, False = test fail). The second parameter is -- the process to test, and the third parameter is the thing to use to -- generate the inputs (passing arbitrary is the simplest thing -- to do). -- -- Here are a couple of example uses: -- --
-- propCHPInOut (==) Common.id (arbitrary :: Gen Int) ---- --
-- propCHPInOut (const $ (< 0)) (Common.map (negate . abs)) (arbitrary :: Gen Int) ---- -- The test starts the process afresh each time, and shuts it down after -- the single output has been produced (by poisoning both its channels). -- Any poison from the process being tested after it has produced its -- output is consequently ignored, but poison instead of producing an -- output will cause a test failure. If the process does not produce an -- output or poison (for example if you test something like the -- Common.filter process), the test will deadlock. propCHPInOut :: (Show a) => (a -> b -> Bool) -> (Chanin a -> Chanout b -> CHP ()) -> Gen a -> Property -- | Takes a CHP program that returns a Bool (True = test passed, False = -- test failed) and forms it into an HUnit test. -- -- Note that if the program exits with poison, this is counted as a test -- failure. testCHP :: CHP Bool -> Test -- | Tests a process that takes a single input and produces a single -- output, using HUnit. -- -- The first parameter is a pure function that takes the input to the -- process, the output the process gave back, and indicates whether this -- is okay (True = test pass, False = test fail). The second parameter is -- the process to test, and the third parameter is the input to send to -- the process. -- -- The intention is that you will either create several tests with the -- same first two parameters or use a const function as the first -- parameter. So for example, here is how you might test the identity -- process with several tests: -- --
-- let check = testCHPInOut (==) Common.id -- in TestList [check 0, check 3, check undefined] ---- -- Whereas here is how you could test a slightly different process: -- --
-- let check = testCHPInOut (const $ (< 0)) (Common.map (negate . abs)) -- in TestList $ map check [-5..5] ---- -- The test starts the process afresh each time, and shuts it down after -- the single output has been produced (by poisoning both its channels). -- Any poison from the process being tested after it has produced its -- output is consequently ignored, but poison instead of producing an -- output will cause a test failure. If the process does not produce an -- output or poison (for example if you test something like the -- Common.filter process), the test will deadlock. testCHPInOut :: (a -> b -> Bool) -> (Chanin a -> Chanout b -> CHP ()) -> a -> Test -- | Like testCHP but allows you to return the more descriptive -- CHPTestResult type, rather than a plain Bool. -- -- Added in version 1.5.0. testCHP' :: CHP CHPTestResult -> Test -- | A helper type for describing a more detailed result of a CHP test. You -- can construct these values manually, or using the '(=*=)' operator. -- -- Added in version 1.5.0. data CHPTestResult CHPTestPass :: CHPTestResult CHPTestFail :: String -> CHPTestResult -- | Checks if two things are equal; passes the test if they are, otherwise -- fails and gives an error that shows the two things in question. -- -- Added in version 1.5.0. (=*=) :: (Eq a, Show a) => a -> a -> CHPTestResult -- | See withCheck. Added in version 1.5.0. data CHPTest a -- | A helper function that allows you to create CHP tests in an assertion -- style, either for use with HUnit or QuickCheck 2. -- -- Any poison thrown by the first argument (the left-hand side when this -- function is used infix) is trapped and ignored. Poison thrown by the -- second argument (the right-hand side when used infix) is counted as a -- test failure. -- -- As an example, imagine that you have a process that should repeatedly -- output the same value (42), called myProc. There are several -- ways to test this, but for the purposes of illustration we will start -- by testing the first two values: -- --
-- myTest :: Test -- myTest = testCHP' $ do -- c <- oneToOneChannel -- myProc (writer c) -- `withCheck` do x0 <- liftCHP $ readChannel (reader c) -- assertCHPEqual (poison (reader c)) "First value" 42 x0 -- x1 <- liftCHP $ readChannel (reader c) -- poison (reader c) -- Shutdown myProc -- assertCHPEqual' "Second value" 42 x1 ---- -- This demonstrates the typical pattern: a do block with some -- initialisation to begin with (creating channels, enrolling on -- barriers), then a withCheck call with the thing you want to test on -- the left-hand side, and the part doing the testing with the asserts on -- the right-hand side. Most CHP actions must be surrounded by -- liftCHP, and assertions can then be made about the values. -- -- Poison is used twice in our example. The assertCHPEqual function takes -- as a first argument the command to execute if the assertion fails. The -- problem is that if the assertion fails, the right-hand side will -- finish. But it is composed in parallel with the left-hand side, which -- does not know to finish (deadlock!). Thus we must pass a command to -- execute if the assertion fails that will shutdown the right-hand side. -- The second assertion doesn't need this, because by the time we make -- the assertion, we have already inserted the poison. Don't forget that -- you must poison to shut down the left-hand side if your test is -- successful or else you will again get deadlock. -- -- A better way to test this process is of course to read in a much -- larger number of samples and check they are all the same, for example: -- --
-- myTest :: Test -- myTest = testCHP' $ do -- c <- oneToOneChannel -- myProc (writer c) -- `withCheck` do xs <- liftCHP $ replicateM 1000 $ readChannel (reader c) -- poison (reader c) -- Shutdown myProc -- assertCHPEqual' "1000 values" xs (replicate 1000 42) ---- -- Added in version 1.5.0. withCheck :: CHP a -> CHPTest () -> CHP CHPTestResult -- | Checks that the given Bool is True. If it is, the assertion passes and -- the test continues. If it is False, the given command is run (which -- should shut down the left-hand side of withCheck) and the test -- finishes, failing with the given String. -- -- Added in version 1.5.0. assertCHP :: CHP () -> String -> Bool -> CHPTest () -- | Like assertCHP but issues no shutdown command. You should only -- use this function if you are sure that the left-hand side of withCheck -- has already completed. -- -- Added in version 1.5.0. assertCHP' :: String -> Bool -> CHPTest () -- | Checks that the given values are equal (first is the expected value of -- the test, second is the actual value). If they are equal, the -- assertion passes and the test continues. If they are not equal, the -- given command is run (which should shut down the left-hand side of -- withCheck) and the test finishes, failing with the a message formed of -- the given String, and describing the two values. -- -- Added in version 1.5.0. assertCHPEqual :: (Eq a, Show a) => CHP () -> String -> a -> a -> CHPTest () -- | Like assertCHPEqual but issues no shutdown command. You should -- only use this function if you are sure that the left-hand side of -- withCheck has already completed. -- -- Added in version 1.5.0. assertCHPEqual' :: (Eq a, Show a) => String -> a -> a -> CHPTest () instance Monad CHPTest instance MonadIO CHPTest instance MonadCHP CHPTest instance Monoid CHPTestResult instance Testable (QuickCheckCHP CHPTestResult) instance Testable (QuickCheckCHP Result) instance Testable (QuickCheckCHP Bool)