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:
- Reading from a channel (including extended reads): that is, calls to
- Writing to a channel (including extended writes): that is, calls to
- Synchronising on a barrier (using
- An alting construct (that is, you can nest alts) such as
priAlt(or the operator versions)
- A sequential composition, if the first event supports alting (i.e. is in this list)
- A call to
every, which joins together several items (see the documentation on
Examples of events that do NOT support alting are:
- Enrolling and resigning with a barrier
- Poisoning a channel
- Processes composed in parallel (using
- Any lifted IO event
- Creating channels, barriers, etc
- Claiming a shared channel (yet...)
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:
- Wait for an integer channel, or 1 second to elapse:
liftM Just (readChannel c) <-> (waitFor 1000000 >> return Nothing)
- Check if a channel is ready, otherwise return immediately. Note that you must use the alt operator with priority here, otherwise your skip guard might be chosen, even when the channel is ready.
liftM Just (readChannel c) </> (skip >> return Nothing)
- Wait for input from one of two (identically typed) channels
readChannel c0 <-> readChannel c1
- Check if a channel is ready; if so send, it on, otherwise return immediately:
(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.
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:
- synchronisation guards (reading from and writing to channels, and synchronising on barriers)
- time-out guards (such as
- dummy guards (
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
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).
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
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
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
this will only count as one member enrolling, even if you have two enrolled
ends! For such a use, look at
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.
- A call to
- A call to
skip, the always-ready guard.
stop, the never-ready guard (will cause the whole
everyto never be ready, since
everyhas to wait for all guards).
- A call to
- A sequential composition where the first item is one of the things in this list.
- A call to
every(you can nest
everyblocks inside each other).
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