-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | A set of high-level concurrency utilities built on Communicating Haskell Processes -- -- In version 1.0.0, this package contains functionality split off during -- the chp 1.x to 2.0 transition. In future, it will contain any new CHP -- features that build on the core library, and that do not require -- access to CHP's internals. This package is closely tied to the chp -- package. @package chp-plus @version 1.2.0 -- | A module containing some useful functions for testing CHP programs, -- both in the QuickCheck 2 framework and using HUnit. module Control.Concurrent.CHP.Test -- | A wrapper around the CHP type that supports some QuickCheck -- Testable instances. See qcCHP and qcCHP'. data QuickCheckCHP a -- | Turns a CHP program into a QuickCheckCHP for use with -- Testable instances. -- -- Equivalent to qcCHP' . runCHP_CSPTrace. 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)
--   
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. 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. 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. (=*=) :: (Eq a, Show a) => a -> a -> CHPTestResult -- | See withCheck. 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)
--   
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. 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. 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. 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. 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) -- | 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 CHP behaviours. See offer for details. 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 m can be thought of as a shortcut for listToMaybe -- $ upTo1 m once :: CHP a -> CHPBehaviour (Maybe a) -- | Offers the given behaviour up to the given number of times, returning -- a list of the results (in chronological order). Like once, when -- the limit is reached, the call to offer is not terminated, so -- you still require an endWhen. upTo :: Int -> CHP a -> CHPBehaviour [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 is like an unbounded upTo. 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 -- | A module of operators for connecting processes together. 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 a) -> CHP a -- | 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 connect, but provides the process a list of items of the -- specified size, and runs it. connectList :: (Connectable l r) => Int -> ([(l, r)] -> CHP a) -> CHP a -- | Like connectList but ignores the results. connectList_ :: (Connectable l r) => Int -> ([(l, r)] -> CHP a) -> CHP () -- | A pair of channels. The main use of this type is with the Connectable -- class, as it allows you to wire together two processes that take the -- exact same channel pair, e.g. both are of type ChannelPair (Chanin -- Int) (Chanout Int) -> CHP (). With the normal Connectable pair -- instances, one would need to be of type (Chanin Int, Chanout Int) -- -> CHP (), and the other of type (Chanout Int, Chanin Int) -- -> CHP (). data ChannelPair l r -- | 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 r; } connectExtra :: (ConnectableExtra l r) => ConnectableParam l r -> ((l, r) -> CHP ()) -> CHP () -- | Like '(=)' but with ConnectableExtra connectWith :: (ConnectableExtra l r) => ConnectableParam l r -> (a -> l -> CHP ()) -> (r -> b -> CHP ()) -> a -> b -> CHP () instance (Eq l, Eq r) => Eq (ChannelPair l r) instance (Show l, Show r) => Show (ChannelPair l r) 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 (Shared Chanin a) (Shared Chanout a) instance ConnectableExtra (Chanin a) (Shared Chanout a) instance ConnectableExtra (Shared Chanin a) (Chanout a) instance ConnectableExtra (Shared Chanout a) (Shared Chanin a) instance ConnectableExtra (Shared Chanout a) (Chanin a) instance ConnectableExtra (Chanout a) (Shared Chanin a) instance Connectable (Shared Chanout a) (Shared Chanin a) instance Connectable (Shared Chanout a) (Chanin a) instance Connectable (Chanout a) (Shared Chanin a) instance Connectable (Shared Chanin a) (Shared Chanout a) instance Connectable (Chanin a) (Shared Chanout a) instance Connectable (Shared Chanin a) (Chanout a) instance ConnectableExtra (Chanin a) (Chanout a) instance Connectable (Chanin a) (Chanout a) instance ConnectableExtra (Chanout a) (Chanin a) instance Connectable (Chanout a) (Chanin a) instance (Connectable l r) => Connectable (ChannelPair l r) (ChannelPair l r) -- | This module contains helper functions for wiring up collections of -- processes into a two-dimensional arrangement. module Control.Concurrent.CHP.Connect.TwoDim -- | A data type representing four-way connectivity for a process, with -- channels to the left and right, above and below. data FourWay above below left right FourWay :: above -> below -> left -> right -> FourWay above below left right above :: FourWay above below left right -> above below :: FourWay above below left right -> below left :: FourWay above below left right -> left right :: FourWay above below left right -> right -- | Wires the given grid of processes (that require four-way connectivity) -- together into a wrapped around grid (a torus) and runs them all in -- parallel. -- -- The parameter is a list of rows, and should be rectangular (i.e. all -- the rows should be the same length). If not, an error will result. The -- return value is guaranteed to be the same shape as the input. -- -- It is worth remembering that if you have only one row or one column -- (or both), processes can be connected to themselves, so make sure that -- if a process is connected to itself (e.g. its left channel connects to -- its right channel), it is coded such that it won't deadlock -- or if -- needed, checks for this possibility using sameChannel. -- Processes may also be connected to each other multiple times -- in a -- two-wide grid, each process's left channel connects to the same -- process as its right. wrappedGridFour :: (Connectable above below, Connectable left right) => [[FourWay above below left right -> CHP a]] -> CHP [[a]] -- | Like wrappedGridFour but discards the return values. wrappedGridFour_ :: (Connectable above below, Connectable left right) => [[FourWay above below left right -> CHP a]] -> CHP () -- | A data type representing four-way diagonal connectivity for a process, -- with channels above-left, below-right, above-right and below-left. data FourWayDiag aboveLeft belowRight aboveRight belowLeft FourWayDiag :: aboveLeft -> belowRight -> aboveRight -> belowLeft -> FourWayDiag aboveLeft belowRight aboveRight belowLeft aboveLeft :: FourWayDiag aboveLeft belowRight aboveRight belowLeft -> aboveLeft belowRight :: FourWayDiag aboveLeft belowRight aboveRight belowLeft -> belowRight aboveRight :: FourWayDiag aboveLeft belowRight aboveRight belowLeft -> aboveRight belowLeft :: FourWayDiag aboveLeft belowRight aboveRight belowLeft -> belowLeft -- | EightWay is simply a synonym for a pair of FourWay and -- FourWayDiag. type EightWay a b l r al br ar bl = (FourWay a b l r, FourWayDiag al br ar bl) -- | Like wrappedGridFour but provides eight-way connectivity. -- -- The note on wrappedGridFour about processes being connected to -- themselves applies here too -- as does the note about processes being -- connected to each other multiple times. If you have one row, a -- process's left, above-left and below-left channels all connect to the -- same process. If you have a two-by-two grid, a process's four diagonal -- channels all connect to the same process. wrappedGridEight :: (Connectable above below, Connectable left right, Connectable aboveLeft belowRight, Connectable belowLeft aboveRight) => [[EightWay above below left right aboveLeft belowRight aboveRight belowLeft -> CHP a]] -> CHP [[a]] -- | Like wrappedGridEight but discards the output. wrappedGridEight_ :: (Connectable above below, Connectable left right, Connectable aboveLeft belowRight, Connectable belowLeft aboveRight) => [[EightWay above below left right aboveLeft belowRight aboveRight belowLeft -> CHP a]] -> CHP () instance (Eq aboveLeft, Eq belowRight, Eq aboveRight, Eq belowLeft) => Eq (FourWayDiag aboveLeft belowRight aboveRight belowLeft) instance (Eq above, Eq below, Eq left, Eq right) => Eq (FourWay above below left right) -- | A module containing a Composed monad. -- -- The Composed monad can be thought of as an equivalent to -- functions elsewhere in chp-plus (especially the -- Control.Concurrent.CHP.Connect module) that support partial -- application of processes when wiring them up. -- -- Binding in this monad can be thought of as "and then wire that like -- this". You compose your processes together with a series of monadic -- actions, feeding processes into each function that wires up the next -- parameter, then taking the results of that action and further wiring -- it up another way. At the end of the monadic block you should return -- the full list of wired-up processes, to be run in parallel using the -- run (or run_) functions. -- -- Here is a simple example. You have a list of processes that take an -- incoming and outgoing channel end and a barrier, and you want to wire -- them into a cycle and enroll them all on the barrier: -- --
--   processes :: [Chanin a -> Chanout a -> EnrolledBarrier -> CHP ()]
--   
--   runProcesses = do b <- newBarrier
--                     run $ cycleR processes >>= enrollAllR b
--   
-- -- The order of the actions in this monad tends not to matter (it is a -- commutative monad for the most part) so you could equally have -- written: -- --
--   processes :: [EnrolledBarrier -> Chanin a -> Chanout a -> CHP ()]
--   
--   runProcesses = do b <- newBarrier
--                     run $ enrollAllR b processes >>= cycleR
--   
-- -- Remember with this monad to return all the processes to be run in -- parallel; if they are not returned, they will not be run and you will -- likely get deadlock. -- -- A little more background on the monad is available in this blog post: -- http://chplib.wordpress.com/2010/01/19/the-process-composition-monad/ module Control.Concurrent.CHP.Composed -- | A monad for composing together CHP processes in cross-cutting ways; -- e.g. wiring together a list of processes into a pipeline, but also -- enrolling them all on a barrier. data Composed a -- | See run and run_ runWith :: Composed a -> forall b. (a -> CHP b) -> CHP b -- | Given a list of CHP processes composed using the Composed monad, runs -- them as a parallel bunch of CHP results (with runParallel) and -- returns the results. run :: Composed [CHP a] -> CHP [a] -- | Like run but discards the results (uses runParallel_). run_ :: Composed [CHP a] -> CHP () -- | Like enroll, this takes a barrier and a process wanting a -- barrier, and enrolls it for the duration, but operates using the -- Composed monad. enrollR :: (Enrollable b p) => b p -> (Enrolled b p -> a) -> Composed a -- | Given an Enrollable item (such as a Barrier), and a list -- of processes, composes them by enrolling them all on the given -- barrier. enrollAllR :: (Enrollable b p) => b p -> [Enrolled b p -> a] -> Composed [a] -- | Like connect but operates in the Composed monad. connectR :: (Connectable l r) => ((l, r) -> a) -> Composed a -- | Wires a list of processes into a pipeline that takes the two channels -- for the ends of the pipeline and returns the list of wired-up -- processes. pipelineR :: (Connectable l r) => [r -> l -> a] -> Composed (r -> l -> [a]) pipelineCompleteR :: (Connectable l r) => (l -> a) -> [r -> l -> a] -> (r -> a) -> Composed [a] -- | Connects together a list of processes into a cycle. cycleR :: (Connectable l r) => [r -> l -> a] -> Composed [a] -- | Like wrappedGridFour, but in the Composed monad. wrappedGridFourR :: (Connectable below above, Connectable right left) => [[FourWay above below left right -> a]] -> Composed [[a]] instance Applicative Composed instance Functor Composed instance MonadIO Composed instance MonadCHP Composed instance Monad Composed -- | A collection of useful common processes that are useful when plumbing -- together a process network. All the processes here rethrow poison when -- it is encountered, as this gives the user maximum flexibility (they -- can let it propagate it, or ignore it). -- -- The names here overlap with standard Prelude names. This is -- deliberate, as the processes act in a similar manner to the -- corresponding Prelude versions. It is expected that you will do -- something like: -- --
--   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.|->|. 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. 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. 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. 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. 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. 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. 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). advanceTime :: (Waitable c, Ord t) => (t -> t) -> Enrolled c t -> 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 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. 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 () -- | 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. -- -- -- -- The problem lies in the buffering inherent in arrows. Imagine if -- f is a single function. f is effectively a buffer of -- one. You can feed it a single value, but no more than that until you -- read its output. However, if you have arr id >>> f, -- that can accept two inputs (one held by the arr id process -- and one held by f) before you must accept the output. -- -- I am fairly confident that the arrow laws are satisfied for the -- definition of equality that given the same single input, they will -- produce the same single output. If you don't worry too much about the -- behavioural difference, and just take arrows as another way to wire -- together a certain class of process network, you should do fine. module Control.Concurrent.CHP.Arrow -- | The type that is an instance of Arrow for process pipelines. -- See runPipeline. data ProcessPipeline a b -- | Given a ProcessPipeline (formed using its Arrow -- instance) and the channels to plug into the ends of the pipeline, -- returns the process representing the pipeline. -- -- The pipeline will run forever (until poisoned) and you must run it in -- parallel to whatever is feeding it the inputs and reading off the -- outputs. Imagine that you want a process pipeline that takes in a pair -- of numbers, doubles the first and adds one to the second. You could -- encode this in an arrow using: -- --
--   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 <<<. 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. 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. 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 action wrappers around channel-ends. -- -- In CHP, there are a variety of channel-ends. Enrolled Chanin, Shared -- Chanout, plain Chanin, and so on. The difference between these ends -- can be important; enrolled channel-ends can be resigned from, shared -- channel-ends need to be claimed before use. But sometimes you just -- want to ignore those differences and read and write from the -- channel-end regardless of its type. In particular, you want to pass a -- channel-end to a process without the process worrying about its type. -- -- Actions allow you to do this. A send action is like a monadic function -- (a -> CHP()) for sending an item, but can be poisoned too. -- A recv action is like something of type CHP a that again can -- be poisoned. module Control.Concurrent.CHP.Actions -- | A send action. See sendAction. Note that it is poisonable. data SendAction a -- | A receive action. See recvAction. Note that it is poisonable. data RecvAction a -- | Sends a data item using the given sendAction. Whether this operation -- can be used in a choice (see alt) is entirely dependent on -- whether the original action could be used in an alt. For all of CHP's -- channels, this is true, but for your own custom send actions, probably -- not. sendAction :: SendAction a -> a -> CHP () -- | Receives a data item using the given recvAction. Whether this -- operation can be used in a choice (see alt) is entirely -- dependent on whether the original action could be used in an alt. For -- all of CHP's channels, this is true, but for your own custom receive -- actions, probably not. recvAction :: RecvAction a -> CHP a -- | Given a writing channel end, gives back the corresponding -- SendAction. makeSendAction :: (WriteableChannel w, Poisonable (w a)) => w a -> SendAction a -- | Given a reading channel end, gives back the corresponding -- RecvAction. makeRecvAction :: (ReadableChannel r, Poisonable (r a)) => r a -> RecvAction a -- | Like makeSendAction, but always applies the given function -- before sending the item. makeSendAction' :: (WriteableChannel w, Poisonable (w b)) => w b -> (a -> b) -> SendAction a -- | Like makeRecvAction, but always applies the given function -- after receiving an item. makeRecvAction' :: (ReadableChannel r, Poisonable (r a)) => r a -> (a -> b) -> RecvAction b -- | Creates a custom send operation. The first parameter should perform -- the send, the second parameter should poison your communication -- channel, and the third parameter should check whether the -- communication channel is already poisoned. Generally, you will want to -- use makeSendAction instead of this function. makeCustomSendAction :: (a -> CHP ()) -> CHP () -> CHP () -> SendAction a -- | Creates a custom receive operation. The first parameter should perform -- the receive, the second parameter should poison your communication -- channel, and the third parameter should check whether the -- communication channel is already poisoned. Generally, you will want to -- use makeRecvAction instead of this function. makeCustomRecvAction :: CHP a -> CHP () -> CHP () -> RecvAction a -- | Acts like a SendAction, but just discards the data. nullSendAction :: SendAction a -- | Acts like a RecvAction, but always gives back the given data item. nullRecvAction :: a -> RecvAction a instance Poisonable (RecvAction c) instance Poisonable (SendAction c)