We provide here a collection of examples of interaction models taken from our test cases, based on areas such as bioinformatics, business and emergency response.

Perhaps the easiest way to understand LCC programming is through design patterns. These are standard ways of structuring clauses that are used to obtain specific forms of interaction. The broad idea is similar to design patterns in more traditional languages but the good news for LCC is that you only need to know a small number of patterns, which you then combine to make more complex programs. The four key patterns are given below.

  • Pattern 1: Interaction
  • The simplest thing we can do with LCC is to specify a message being sent from one peer to another. To do this we decide the role (r1) being taken by the sender; then write M out a(r2, Y) if C to describe the message, M, being sent out to the recipient, Y, which is expected to receive it in role r2. The constraint C1 is used to determine whether this message can be sent by the sender, and it often is used also to determine values for any variables that appear in M.. In the specification of the recipient's role we write C2 if M in a(r2, X) to describe the message, M, being received, with C2 giving a constraint that should hold as a consequence of receiving it.

    a(r1,X) ::
         . . .
         M ⇒ a(r2, Y ) ← C1
         . . .
                                                     (1.8)
    a(r2, Y ) ::
         . . .
         C2 ← M ⇐ a(r2,X)
         . . .
    

    An example of using this pattern is an interaction that sends a message, M, to a recipient, Y, where the choice on M is made by the constraint message(M) and the choice of recipient is made by the constraint recipient(Y). Acceptance of the message by the recipient is determined by the constraint accept(M).

    a(sender, X) ::
         M ⇒ a(recipient, Y) ← message(M) and recipient(Y)
                                                                     (1.9)
    a(recipient, Y) ::
         accept(M) ← M ⇐ a(sender, X)
    
  • Pattern 2: Sequence
  • Usually we want to put an ordering on the sequence of events that can occur as part of a role. To do this we use the "then" operator to say that the earlier event, E1, comes before the later event, E2.

    a(r,X) ::
         . . .
         E1 then                                          (1.10)
         E2
         . . .
    

    An example that uses this pattern twice is when the recipient of the message returns a message to the sender, where response(M1, M2) is a constraint determining the recipient's response message, M2, from the sender's message, M1.

    a(sender,X) ::
         M1 ⇒ a(recipient, Y ) ← message(M1)and recipient(Y ) then
         accept(M2) ← M2 ⇐ a(recipient, Y )
                                                                 (1.11)
    a(recipient, Y ) ::
         accept(M1) ← M1 ⇐ a(sender,X) then
         M2 ⇒ a(sender,X) ← response(M1,M2)
    
  • Pattern 3: Choice
  • We may want a peer taking some role, r, in an interaction to make a choice about the course of its interaction with other peers. This is done by writing E1 if C1 or E2 if C2 to say that the interaction described by E1 should be done under the conditions stipulated by constraint C1 or the interaction described by E2 should be done under the conditions stipulated by constraint C2. The choice we are making here is a committed choice, meaning that if C1 is satisfied then the alternative choice (E2 if C2) will not be attempted.

    a(r,X) ::
         E1 ← C1
         or                                              (1.12)
         E2 ← C2
         . . .
    

    An example of this pattern is when a buyer wants to send a message to a seller accepting some Offer (received earlier in the definition of the buyer role) if it is acceptable or otherwise it sends a message to the seller rejecting that Offer if it is unacceptable.

    a(buyer, X) ::
         . . .
         accept(Offer) ⇒ a(seller, Y) ← acceptable(Offer)             (1.13)
         or
         reject(Offer) ⇒ a(seller, Y) ← unacceptable(Offer)
    

    Since LCC makes committed choices, we know in this example that if acceptable(Offer) is satisfied then the second option (in which the peer attempts to satisfy unacceptable(Offer)) will not be attempted, so if testing unacceptability is not important then we might shorten this example to:

    a(buyer, X) ::
         . . .
         accept(Offer) ⇒ a(seller, Y) ← acceptable(Offer)             (1.14)
         or
         reject(Offer) ⇒ a(seller, Y)
    
  • Pattern 4: Recursion
  • Often we want an interaction to be controlled by some data structure, for example we might want to have a similar sub-interaction for each of the elements in a list (as in the basic example above). The pattern below describes this. In the pattern r(A) is a role, r, with the data structure as its argument, A. Somewhere within the definition of the of the role appears a constraint, R(A, Ar), that reduces A to some "smaller" structure, Ar. Then the role recurses as r(Ar). Normally there also is an alternative choice for when the data structure does not reduce any further but meets some test, P(A), that it is has reached some terminating state.

    a(r(A),X) ::
         (. . . R(A,Ar) . . .
         a(r(Ar),X))                                           (1.15)
         or
         (. . . P(A) . . .)
    

    One example of using this pattern is an interaction that sends as a message each element, M, from a list [M1,...] to peer p2 in role r2.

    a(r(A), X) ::
         (M ⇒ a(r2, p2) ← A = [M|Ar] then
         a(r(Ar), X) )                                       (1.16)
         or
         (null ← A = [] )
    

    A second example is an interaction that sends N messages to peer p2, each with the same content, M. Here, N and M are parameters to the role, r.

    a(r(N, M), X) ::
         (M ⇒ a(r2, p2) ← N > 0 and N1 is N - 1 then
         a(r(N1, M), X))                                               (1.17)
         or
         (null ← N =< 0)
    
Egglue Powered