The shared protocols describing interactions are called Interaction Models and are written in Lightweight Coordination Calculus (LCC). This, as the name suggests, is designed to be very lightweight and to convey an interaction simply without getting bogged down in complexity. However, since there may be details that are pertinent to an interaction that cannot be expressed in LCC, we allow annotation files to be written for IMs. We do not discuss this here in this simple description of LCC, but details of this are available.
a(requester, A) ::
ask(X1) => a(informer, p2) <-- query_from(X1, p2) then
tell(X1) <= a(informer, p2) then
ask(X2) => a(informer, p3) <-- query_from(X2, p3) then
tell(X2) <= a(informer, p3)
a(informer, B) ::
ask(X) <= a(requester, B) then
tell(X) => a(requester, B) <-- know(X)
Figure 1. An example informer/requester interaction model.
Figure 1 shows a simple diagram for discovering information: a requester asks for information and an informer responds.
This simple IM is sufficient to illustrate the three key aspects to an IM: roles, messages and constraints. Firstly, we should note that in LCC, variables are identified by beginning (or consisting of) an uppercase letter, whereas constants begin with (or consist of) a lowercase letter.
Roles: IMs must consist of two or more roles, with each role describing the necessary actions for one of the peers that will take part in the interaction. Role identifiers are of the form a(role,ID). For example, in Figure 1, the first role is requester, and this will be played by a peer with ID A. A is a variable that will become instantiated with the appropriate peer ID when this is known. It would be possible to make this ID a constant, and thus the role would only be playable by the particular peer identified by that constant, but this is contrary to ideal of reusing IMs so is not generally encouraged.
Messages: These are indicated by the double arrow. To the left of this double arrow is message that is to be passed and to the right is the role to which it is to be sent or received from. Double arrows pointing to the right (towards the role) indicate messages going to that role, and vice versa. Within an IM, most messages will contain one or more variables that are not determined by the IM: for example, in Figure 1, the variable X is passed around. The value of this variable is only determined during run-time: therefore, the IM is reusable in different situations.
Any message that is sent in an IM must also be received by the appropriate role within that IM, and thus all messages appear twice, once as a sent message within the sender's role and once as a received message within the receiver's role. When there are only two roles, this makes the message passing in those roles completely symmetric, as can be seen in Figure 1.
Constraints: These are put on messages, appearing to the right of them in an IM and pointing to the relevant message with a single arrow, and have two functions:
- to explain how the variables in the messages should be instantiated;
- to limit the circumstances under which a message can be sent: it can only be sent if the constraint can be satisfied by the peer playing the role.
For example, during run-time, we do not wish to pass around the variable X; instead, it should be instantiated to a suitable value. This is done by the constraint query(X). So the peer that takes on the role of requester must be able to satisfy this constraint, and in doing so, it will instantiate the variable X to whatever it wishes to query about.
Any OK peer is capable of passing any message, provided the necessary constraints are satisfied, so the ability to play a particular role is equivalent to the ability to satisfy all the constraints on messages in that role.
