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 above; if an action rule is found, then the agent is attached to the channels of E, and the agent 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. Actions cannot 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; instead, it sleeps until the next event is posted.

Agents behave in an event-driven fashion. At the entry and exit points of each predicate, the system checks whether an event has been posted. If so, the current predicate is interrupted, and control is moved to the agents that the event activates. 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 of the sleeping agents that are watching the event in the system will be activated, after which the event is erased, ensuring that agents that are generated later will not be responsive to this event. The activated agents that are attached to a channel are added to the chain of active agents in the first-generated-first-added order, unless the event was posted by using the built-in post_event_df. Since there may exist multiple events on different channels at a time, and since an agent can post events in its action, the ordering of agents is normally unpredictable.

There is no primitive for explicitly killing agents. As described above, an agent never disappears, as long as action rules are applied to it. An agent only vanishes 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 that is defined here can only handle one event posting. After it handles an event, it binds the variable Flag. Therefore, when a second event is posted, the action rule is no longer applicable, and, hence, the matching clause after it will be selected. Note 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 that case, the agent will stay forever. If users want to kill the agent immediately after it is activated once, then users must 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 will be killed after the failure of the test var(Flag).

Neng-Fa Zhou 2013-01-25