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.
@atomicThe @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@parallelThe @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”) .