ContextChat example

Context-oriented programming

The example proposed here is taken from Salvaneschi, G. et al. ContextErlang: Introducing Context-oriented Programming in the Actor Model as a pragmatic assessment on the expressiveness of the AIOC language with relation to distributed dynamic context-oriented systems.

Context-oriented evolve from Aspect-Oriented adaptation introducing mechanisms to enforce the coherent application of aspects.

In the paper the authors propose the example of ContextChat to highlight the features of context-oriented programming applied to the Erlang actor model. In particular the authors introduce a fixed stack of adaptation procedures. The slots that compose said stack describe the behaviour of adaptable procedures (also called variations). Activatable slots contain one variation which can independently activated or not, switch slots contain several mutually exclusive variations, and free slots contain an undefined single variation which can be acquired and assigned from other programs.

ContextChat

In ContextChat connected clients exchange messages in real time. The system implements advanced features as context variants:
  • offline reception: if a user goes offline, other users can still send messages to him. The system saves offline messages and shows them to the recipient when s/he returns online. Offline and online statuses are described in a switch slot, therefore mutually exclusive:
  • backup feature: the user can activate (activatable slot) a backup mode that saves all messages (sent and received) on a remote server;
  • tracing feature: the system can activate (activatable slot) a tracing mode that collects information on client communication to handle network communication in a more efficient way (e.g., using internal messages in place of network ones);
  • text effects: users can submit (free slot) customisable filters to the messages (e.g., for text emphasising, emoticons, etc.).
Implementing ContextChat, modelling contexts with scopes

Let us analyse how the different behaviour described by activatable, switch, and free slots maps to AIOCJ scopes.

Activatable and free slots coalesce in the same type of scopes as in AIOCJ the source of variations (rules, in AIOCJ context) is remote (the set of Adaptation Servers). Activation of such variation is based on variables belonging to the environment (prefixed with E.), to the scope, which are non-functional (prefixed with N.), and from the status of the leader of the adaptation.

Switch slots require a choice in the design of the adaptation wrt the basic and the altered mutually exclusive behaviours.

Referring to the offline/online statuses of ContextChat, we assume that "online" is the basic behaviour which alternates with the "offline" one on user's preference. Therefore, the choreography contains the basic behaviour (online) and, depending on the availability of Adaptation Servers and the user's status, mutually exclusive behaviours will overwrite the basic one. Since after adaptation scope boundaries are removed, each time the basic behaviour adapts only the matching rule overwrites its behaviour, excluding any other applicable variant.

To keep the example simple and since activatable slots directly map to scopes, we implement the example of ContextChat with the online/offline variants and the free slot for text emphasising.

Online/Offline switch

Since Online status is the basic behaviour, the choreography implements it as its default. However, the programmer can specify that the procedure for acquire a user's message can be changed (below). In this case the rule always applies and augments the functionality of the interaction with the user which can either send a message or go offline by writing "exit".

scope @user1{
  msg@user1 = getInput( name + ": insert a message" )
} prop { N.scope_name = "get_msg" }
// rule applies on any scope with property 
// scope_name equal to "get_msg"
rule {
  on { N.scope_name == "get_msg" }
  do { msg@user1 = getInput( name + 
    ": write a message or 'exit' to go offline" );
    if( msg == "exit" )@user1{
      status@user1 = "offline"
 }}}

Notably, the scope, led and participated only by user1 indicates as N.scope_name = "get_msg" its only non-functional property. user2 shows an equivalent scope (with the non-functional property N.scope_name = "get_msg"), yet the adaptation mechanisms of AIOCJ apply the corresponding rule to the right scope as it checks applicability of rules on the participants to the scope. Since in this case the leaders are also the only participants to the scope, no further properties are needed to ensure coherent application of adaptation.

Subsequently, the scope with N.scope_name = "status_switch_user1" overwrites the sending procedure. The rules procedure waits for the user to return online and displays the messages received in the meanwhile. Notably the rule applies only if the status variable of the leaders is set to "offline".

scope @user1{
  send: user1( msg ) -> user2( msg );
  {
    scope @user2{
      _r@user2 = show( name + ": " + msg )
  } prop { N.scope_name = "display_msg" }
  |
  c@user1 = getInput( name + 
  ": do you want to continue [y/n]? ");
  if( c == "n" )@user1{
    continue@user1 = false | 
    continue@user2 = false
}}} 
prop { N.scope_name = "status_switch_user1" }
// rule applies when the user is offline. It acts as 
// a switch to return the status online
// when the user returns online it displays the 
// stored messages
rule {
  on { status == "offline" and 
  N.scope_name == "status_switch_user1" }
  do { _r@user1 = show( name + 
  ": click ok to go online" );
    status@user1 = "online";
    if( stored_msg_count > 0 )@user1 {
      _r@user1 = show( name + 
      " received: " + stored_msg );
      stored_msg@user1 = "";
      stored_msg_count@user1 = 0
    }
  }
}  

Finally, if the user is online and sends a message, the addressee (user2, in case of user1) enters the scope with N.scope_name = "display_msg". The scope serves a twofold purpose: in case the addressee is offline, the overwriting rule (which is led and participated by user2) stores the message into a storage variable for future visualisation, if otherwise user2 is online the text-effect rule applies. Notably text-effects can stack as a the adaptation code of the rule bears a nested scope with the same properties of the adapted scope, beside the one N.emph_effect = "applied" which prevents the infinite application of the same rule.

// rule applies when the user is offline. It stores 
// the message for future display
rule {
	on { status == "offline" and N.scope_name == "display_msg" }
	do {
	  if( stored_msg_count > 0 )@user1{
	  	stored_msg@user1 = stored_msg + "; " + msg
	  } else {
	    stored_msg@user1 = msg
	  };
	  stored_msg_count@user1 = stored_msg_count + 1
}}
// rule applies when the user is online. It adds text effects to the 
// displayed message we apply text effect only on online display
rule {
	on { status == "online" and 
	N.scope_name == "display_msg" and 
	N.emph_effect != "applied" }
	do {
  	msg@user1 = "*" + msg + "*";
  	scope @user1{
    	_r@user1 = show( msg )
  	} prop { N.scope_name = "display_msg", N.emph_effect = "applied" 
}}}

Code

Click on the buttons below to download the code of this example.