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:
Examples of events that do NOT support alting are:
It is not easily possible to represent this at the type level (while still making CHP easy to use). Therefore it is left to you to not try to alt over something that does not support it. Given how much of the library does support alting, that should hopefully be straightforward.
Here are some examples of using alting:
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).
|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.|
|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 (<->)|
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:
1. synchronisation guards (reading from and writing to channels, and synchronising on barriers)
2. time-out guards (such as waitFor)
There exists priority when comparing dummy guards to anything else. So for example,
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).
|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 (</>)|
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).
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.
Timeouts (e.g. waitFor) are currently not supported. You can always get another process to synchronise on an event with you once a certain time has passed.
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
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
|Produced by Haddock version 2.4.2|