Operational semantics

An action rule 'H,G,{E} => B' is said to be applicable to an agent $\alpha$ if $\alpha$ matches H and the guard G succeeds. For an agent, the system searches for an applicable rule in its definition sequentially from the top. If no applicable rule is found, the agent fails; if a matching clause is found, then the agent is rewritten to the body of the clause as described before; if an action rule is found, then the agent is attached to the channels of E and is then suspended waiting until an event of a pattern in E is posted. When an event is posted, the conditions in the guard are tested again. If they are satisfied, then the body B is executed. No action can succeed more than once. The system enforces this by converting B into once(B). When B fails, the original agent fails as well. After B is executed, the agent does not vanish but instead turns to sleep until next event is posted.

Agents behave in an event-driven fashion. At the entry and exit points of every predicate, the system checks to see whether there is an event that has been posted. If so, the current predicate is interrupted and control is moved to the activated agents of the event. After the agents finish their execution, the interrupted predicate will resume. So, for the following query:

      echo_agent(X), {event(X,Message)} => write(Message).

      ?-echo_agent(X),post_event(X,ping),write(pong)
the output message will be ping followed by pong. The execution of write(pong) is interrupted after the event event(X,ping) is posted. The execution of agents can be further interrupted by other postings of events.

There may be multiple events pending at an execution point (e.g., events posted by non-interruptible built-ins). If this is the case, then a watching agent has to be activated once for each of the events.

When an event is posted, all the sleeping agents watching the event in the system will be activated and the event is erased after that so that no agent generated later will be responsive to this event. The activated agents attached to a channel are added to the chain of active agents in the first-generated-first-added order unless the event was posted using the built-in post_event_df. As there may exist multiple events on different channels at a time and an agent can post events in its action, the ordering of agents is normally unpredictable.

There is no primitive for killing agents explicitly. As described above, an agent never disappears as long as action rules are applied to it. An agent vanishes only when a matching clause is applied to it. Consider the following example.

      echo_agent(X,Flag), var(Flag), {event(X,Message)} => 
          write(Message),Falg=1.
      echo_agent(X,Flag) => true.
An echo agent defined here can only handle one event posting. After it handles an event, it binds the variable Flag. So, when a second event is posted, the action rule is no longer applicable and hence the matching clause after it will be selected. Notice that the matching clause is necessary here. Without it, an agent would fail after a second event is posted.

One question arises here: what happens if there will never be another event on X? In this case, the agent will stay forever. If we want to kill the agent immediately after it is activated once, we have to define it as follows:

      echo_agent(X,Flag), var(Flag), {event(X,Message),ins(Flag)} => 
          write(Message),Falg=1.
      echo_agent(X,Flag) => true.
In this way, the agent will be activated again after Flag is bound to $1$, and be killed after the failure of the test var(Flag).

Neng-Fa Zhou 2012-01-03