Selections

 

It is possible to code selection statements and loops using only the original AgentSpeak(L)  language. We will explain how to do this in the sections below.

Rule-based Selection

The basic syntax of AgentSpeak(L) is aimed at contextual handling of events, where the context is derived from the state of the agent. This maps naturally onto the idea of selection in programming languages. The basic idea is that you create a set of rules – one rule per option – that handle the same event (usually a goal addition event). The selection of the appropriate rule is then left to the interpreter.

For example, consider a program that is designed to handle the event that a ball has been lost. This event can be handled in a number of different basic ways: * if the player is near the ball and the opposition has is, then the player should tackle the opposition player * if the player is near the ball and it is free, then it should try to control the ball * if the player is near the ball and its team has the ball, then it should move into space. There are many more possible scenarios for the decision that the player must make. The above examples are, however, enough to illustrate the idea.


rule +!decide() :
  is("ball", "near") & has("ball", string X) &
  is(X, "opposition") {
    !tackle(X);
}

rule +!decide() :
  is("ball", "near") & is("ball", "free") {
    !controlBall();
}

rule +!decide() :
  is("ball", "near") & has("ball", string X) &
  is(X, "team_member") {
    !moveToSpace();
}

This type of decomposition is natural and expressive in the sense that there is a clear goal (!decide()); there area many ways of handling that goal; and each approach is encoded as a rule. Unfortunately, there are other scenarios where this type of selection is less friendly:

initial !printIfEven(5);

rule +!printIfEven(int X) : X % 2 == 0 {
    console.println("X = " + X);
}

rule +!printIfEvent(int X) { }

The above program is a simple program to print out a number only if it is even. Unfortunately, implementing this requires 2 rules. The first rule encodes the logic for printing out an even number and the second rule encodes the logic for not printing out odd numbers.Even though you are only required to print the number if it is even, both possibilities must be catered for in the code because failure to match an event to a rule causes the subgoal to automatically fail. This may not be an issue in the program above, but if the !printIfEven(…) goal was called as a subgoal, then it would matter.

Selection with If Statements

For many programs, the rule-based approach can result in an explosion of rules that quickly become unmanageable since each rule must have an associated event with a unique name. This proliferation of rules can make it difficult to locate the rules you are interested in and can make effective organisation of the code more complex. Further, the flow of the program often becomes more difficult to follow which can lead to a increase in coding errors. As a result, ASTRA includes a second, more standard (in a programming languages sense) approach to selection – an if statement.

For example, consider the program to print out a number only if it is even. This program can be rewritten as follows using an if statement:

initial !printIfEven(5);

rule +!printIfEven(int X) {
    if (X % 2 == 0) console.println("X = " + X);
}

The resulting program is far more compact and fits more closely to the standard solution. Of course, it is possible to use only if statements in code and not to make use of rule-based selection, but this also has drawbacks. For example, if we look at the first example above:

rule +!decide() {
    if (is("ball", "near") &
        has("ball", string X) & is(X, "opposition")) {
        !tackle(X);
    } else if (is("ball", "near") & 
        is("ball", "free")) {
        !controlBall();
    } else if (is("ball", "near") &
        has("ball", string X) & 
        is(X, "team_member")) {
        !moveToSpace();
    }
}

While this program does exactly the same thing as the first program, there is an argument that it is less readable because the single rule can become highly complex (especially if each branch was more complex than simply calling a subgoal).

When to Use Which Format

One of the goals in designing ASTRA has been to try to provide flexibility to write code that is easy to maintain and comprehend. Rule-based selections can result in large numbers of rules that have small plan implementations. Conversely, if statement based selections can result in fewer rules but with more complex plan implementations. As a general guide, it is recommended that rules be used to define coherent (partial) behaviours and if statements be used within those behaviours.

An analogy to procedural programming is the idea of a method/function. Methods encapsulate partial object behaviours and rules are the ASTRA equivalent of this. Algorithms, on the other hand define strategies for implementing partial behaviours and plan implementations are the ASTRA equivalent of this. When this analogy is made, there is a natural connect that follows the following general guidance: if you would use a method/function in a procedural language then it should probably be a rule. If you would use an if statement in a procedural language, then you should do the same in ASTRA.