Analogously to Java, AgentSpeak(L++) also supports the concept of annotations.
In AgentSpeak(L++) → plans and logic rules can be modified in their execution behaviour by adding annotation(s) to them.
Plans: Our plan grammar specifies that (optional) annotations, prefixed with an @
symbol, can be placed in front of plan trigger, i.e.
@annotation
+!plan <-
…
.
Rules: Analogously LightJason(L++) allows adding annotations to rules.
@annotation
rule
:- conditions
:- conditions
…
.
Note: Multiple annotations can be combined, e.g. for plans
@annotation1 @annotation2 +!plan <- … .
We currently support two built-in annotations, i.e. @parallel
and @atomic
, which are explained in the following in further detail.
@atomic
The @atomic
annotation defines a plan to always succeed by default. This behaviour is useful in cases where failure of → actions can be expected but do not constitute erroneous behaviour or results.
Usage:
@atomic
+!plan <-
…
.
The plan will succeed regardless of failing actions or sub-plans.
For a plan
@atomic
+!plan <-
action1();
!!immediate_subplan;
!postponed_subplan;
action2();
.
the execution flow is as depicted below@parallel
The @parallel
annotation defines a plan to execute every action and → goal in its body in parallel, via fork-join mechanism. This behaviour is useful to speed up execution of independent actions and sub-plans.
Notes:
- If any of the parallel executed body elements fails, the whole plan fails.
- The annotation only applies to plans triggered by
!!
and executed in the current cycle. Plans triggered via!
will be independently executed in the following cycle. For more information on triggering see → Plan Triggering Techniques.
Usage:
@parallel
+!plan <-
…
.
For a plan
@parallel
+!plan <-
action1();
!!immediate_subplan;
!postponed_subplan;
action2();
.
the execution flow is as depicted belowThe agent script
!main.
+!main <- !!plan << generic/print(“plan failed!”) .
+!plan <- generic/print(“foo”); !!subplan1; !!subplan2; generic/print(“bar”) .
+!subplan1 <- generic/print(“sub-plan1”); fail .
+!subplan2 <- generic/print(“sub-plan2”) .
will only yield
foo
sub-plan1
plan failed!
as +!subplan1
fails and the execution stops. The <<
represents a → repair action.
Adding @parallel
to +!plan
will execute every goal trigger and action in parallel, yielding
bar
sub-plan1
foo
sub-plan2
plan failed!
(ordering of output might vary). It can be observed that, despite executing every element in the body of +!plan
, the whole plan still fails, as +!subplan1
fails.
Further adding @atomic
to +!plan
will result in successful execution of +!plan
, returning
foo
bar
sub-plan1
sub-plan2
(ordering might vary). The same effect could also be achieved by preventing !subplan1
to fail via @atomic
, which is left as an exercise to the reader.
The complete example with @atomic
and @parallel
would be
!main.
+!main <- !!plan << generic/print(“plan failed!”) .
@atomic @parallel +!plan <- generic/print(“foo”); !!subplan1; !!subplan2; generic/print(“bar”) .
+!subplan1 <- generic/print(“sub-plan1”); fail .
+!subplan2 <- generic/print(“sub-plan2”) .