This document describes the features related to the definition of a behavior in SARL. Before reading this document, we recommend that you read the General Syntax Reference, and the Agent Reference.
A Behavior is the specification of a collection of behavior units (or event handlers). This Behavior may be used by an agent for building its global behavior.
A behavior is declared with the behavior
keyword. In the behavior’s body block, we can declare Mental States
(in the form of attributes), Actions and Behaviors.
The following code illustrates the definition of a behavior named [:behaviortype1], and that is empty.
Basically, this behavior does nothing, and does not react on events.
behavior MyBehavior {
}
The mental state of an agent is composed by the data in the knowledge of the agent. A behavior may contain a part of this mental state. Most of the time, it is implemented as a collection of attributes.
According to the General Syntax Reference, the attributes may be
modifiable (when declared with the var
keyword), or unmodifiable (when declared with the
val
keyword).
behavior MyBehavior {
// Defining a modifiable element of the mental state
var mentalStateElement1 : String
// Defining an unmodifiable element of the mental state
val mentalStateElement2 : boolean = true
}
It is allowed to define actions (methods) in the behavior. The syntax described in the General Syntax Reference is used.
The example below illustrates the creation of type actions.
behavior MyBehavior {
uses Logging
// Defining an action without parameter nor return type
def myAction1 {
info("Hello world")
}
// Defining an action with a variadic parameter and no return type
def myAction2(param : int*) {
info("params are " + param)
}
}
In some use cases, it is useful to specialize the definition of a behavior. This mechanism is supported by the inheritance feature of SARL, which has the same semantic as the inheritance mechanism as the Java object-oriented language.
The extended behavior is specified just after the extends
keyword.
Very Important Note A behavior type can extend only one other behavior type. This is close to the constraint on the extension of classes in the Java language.
In the following code, a first behavior is defined with the name MyBehavior
and an attribute named attr
.
A second behavior MySubBehavior is defined as the extension of the first behavior. It contains a function named
action
, which is displaying the inherited attribute.
behavior MyBehavior {
protected var attr : String
}
behavior MySubBehavior extends MyBehavior {
uses Logging
def action {
info(attr)
}
}
A behavior is always owned by an agent. Consequently, it is mandatory to pass the agent as parameter of the behavior’s constructor.
In the following example, a behavior of type MyBehavior
is instanced (with the agent as the
owner/parameter). This new behavior is then registered into the agent for enabling the reception of
the events in the behavior.
agent MyAgent {
uses Behaviors
on Initialize {
// Create the instance of the behavior
var beh = new MyBehavior(this) // <- the parameter is the agent
// Register the behavior for receiving the events.
// This function is given by the Behaviors capacity
registerBehavior(beh)
}
}
Modifiers are used to modify declarations of types and type members. This section introduces the modifiers for the behavior. The modifiers are usually written before the keyword for defining the behavior.
The complete description of the modifiers’ semantic is available in this section.
A behavior may be declared with one or more modifiers, which affect its runtime behavior:
public
: the behavior is accessible from any other type (default);abstract
: the behavior is abstract and cannot be instanced.final
: avoid to be derived.Examples:
public behavior Example1 {
}
package behavior Example2 {
}
abstract behavior Example3 {
}
final behavior Example4 {
}
The modifiers for the fields in a behavior are:
public
: the field is accessible from everywhere;protected
: the field is accessible within the same package, and derived agents;package
: the field is accessible only within the same package of its agent;private
: the field is accessible only within its agent (default).static
: the field is a class field, not an instance field.Examples:
public var example0 : Object
protected var example1 : Object
package var example2 : Object
private var example3 : Object
static var example4 : Object
The modifiers for the methods in a behavior are:
public
: the method is accessible from everywhere;protected
: the method is accessible within the same package, and derived classes (default);package
the method is accessible only within the same package as its class;private
: the method is accessible only within its class.abstract
: the method has no implementation in the class.dispatch
: the method provides an implementation for the dispatch method mechanism.final
: the method cannot be overridden in derived classes.static
: the method is a class method, not an instance method.Examples:
// Public access function
public def example0 { }
// Protected access function
protected def example1 { }
// Package access function
package def example2 { }
// Private access function
private def example3 { }
// Abstract function
abstract def example4
// Not-overridable function
final def example5 { }
// Dispatch functions
dispatch def example7(p : Integer) { }
dispatch def example7(p : Float) { }
// Static / Class function
static def example8 { }
The behaviors of an agent correspond to the units that are executed by the agent for exhibiting its general behavior.
The Behavior statement permits specifying a subset of the agent’s behavior inside a single syntactic entity. Two types of behaviors are considered:
The definition of the reactive behaviors is based on the event handling mechanism of SARL. Events may be emitted in spaces, and received by the agents, and their behaviors, belonging to these spaces. A behavior may indicate that it is interesting for receiving an event by specifying an event handler using the following syntax:
on EventName [ Guard ] {
Statements
}
EventName
is the name of event to wait for Guard
is the optional specification of a predicate
that may be true for executing the Statements
.
The statements are executed only if an event with the given name is received, and if the guard is true.
In the guard and the statements, it is possible to use the instance of the received event:
the occurrence. This instance is represented by the occurrence
keyword. It is an implicit
variable as the keywords this
and it
.
When a behavior is ready to be executed by the runtime environment, usually when it
is registered in its owning agent, it receives the Initialize
event.
This event is defined as:
event Initialize {
val parameters : Object[]
val spawner : UUID
}
It contains the list of the parameters given that are never set for behaviors.
behavior MyBehavior {
uses Logging
on Initialize {
info("I'm initializing my behavior")
}
}
Because Initialize
is an event, the handler in the behavior could use a guard. This feature enables
the developer to write different initialization blocks depending on the guards of the handlers.
In the following example, the first event handler is executed when the Initialize
event has
no parameter. The second event handler is executed when the event has at least one parameter.
behavior MyBehavior {
uses Logging
on Initialize [ occurrence.parameters.empty ] {
info("First initialization")
}
on Initialize [ ! occurrence.parameters.empty ] {
info("First initialization")
}
}
The on Initialize
event handler in behaviors is a bit special, as it is the code run when a behavior is attached to its agent.
As such, its execution is more “synchronous” than other on-behavior rules. In particular:
on Initialize
, will not be processed until that
on Initialize
code finishes. So, your behavior initialization should not depend
(and wait) on any fired event being processed, as they won’t!on Initialize
, the spawn instructions will return only
after the agent has been created. However, creation of the agent (i.e., of the
corresponding object) does not include initialization of the agent via its
on Initialize
handler. Said so, the Java thread manager may process those
initialization processes of the new agent before continuing with the execution
of the spawning agent (and this seems to be the case in many Linux boxes
where the executor service of Java tends to have the same behavior during
all the runs). If you change computer, it may be different.It is allowed to declare multiple initialization handlers into a single behavior type, as illustrated by:
behavior Beh1 {
uses Logging
on Initialize {
info("1")
}
on Initialize {
info("2")
}
on Initialize {
info("3")
}
}
According to the SARL operational semantic, the three event handlers for Initialize
are run in parallel.
The initialization event handlers are not constructors (as defined in object-oriented programming paradigm),
they are reacting to the receiving of an Initialize
occurrence.
The example in the previous section could be extended in order to illustrate how the initialization handlers
are run when the type of the behavior (here Beh2
) is declared within a inheritance hierarchy.
behavior Beh2 extends Beh1 {
uses Logging
on Initialize {
info("4")
}
on Initialize {
info("5")
}
}
According to the SARL operational semantic, all the initialization handlers are run in parallel.
In the previous example, five event handlers will be run: three are defined into Beh1
, and
two are defined into Beh2
. This mechanism is generalized to all the events within a behavior.
The counterpart of Initialize
is the event Destroy
. This event is defined as:
event Destroy {
}
Example:
behavior MyBehavior {
uses Logging
on Destroy {
info("Destroying the behavior")
}
}
As for Initialize
, the handlers of the Destroy
event could be guarded.
In the following example, the first event handler is executed when the Destroy
is received
and there is resource stored in the corresponding field. The second event handler is executed
when there is no resource.
behavior MyBehavior {
uses Logging
var resource : Object
on Destroy [ resource !== null ] {
info("Destroying the behavior when there is a resource")
}
on Destroy [ resource === null ] {
info("Destroying the behavior when there is no resource")
}
}
The reactive behavior is specified with a collection of event handlers. The principle of a reactive behavior is to execute a part of the behavior when something has happening in the behavior, the agent or in its environment.
In the following example, the behavior is reacting to the reception of the SomethingChanged
event.
As for all the event handlers, it could be guarded by a predicate.
behavior MyBehavior {
uses Logging
on SomethingChanged {
info("Reactive behavior")
}
}
When an event is received and the guard of the corresponding handler is true, the event handler is said to be triggered.
When multiple event handlers are triggered at the same time, they are all executed in parallel.
In the following example, the two handlers for the SomethingChanged
event are executed in parallel.
behavior MyBehavior {
uses Logging
on SomethingChanged {
info("First reactive behavior")
}
on SomethingChanged {
info("Second reactive behavior")
}
}
A proactive behavior is a part of the global behavior of an agent that the agent is deciding to execute by itself. The execution of a reactive behavior is initiated by a part of the code external to this behavior. In opposite, the initiator of the execution of a proactive behavior is the agent itself.
In SARL, a proactive behavior is a behavior that is scheduled
by the agent or one of its behaviors. The schedule mechanism is provided by the
Schedules
built-in capacity.
In the following example, the agent execute its proactive behavior every second.
behavior MyBehavior {
uses Schedules, Logging
on Initialize {
every(1000) [
info("Run a pro-active behavior")
]
}
}
Since the version 0.14
of SARL, it is possible to define an event with generic types.
The counterpart of this type of event definition is related to the definition of generic types in the event handlers.
The specific of the generic types within the event handlers could be assimilated to an implict guard condition.
In other word, the event handler is run only if the received event is matching the specific generic types.
Let the following example in which the event MyGenericEvent
is defined with two generic types T1
and T2
.
Several event handlers have been defined for illustrating multiple cases.
event MyGenericEvent<T1, T2 extends Number> {
var field0 : T1
var field1 : T2
}
behavior MyBehavior {
uses Logging
on MyGenericEvent {
info("Run whatever the values of field0 and field1")
}
on MyGenericEvent<?, ?> {
info("Run whatever the values of field0 and field1")
}
on MyGenericEvent<String, ?> {
info("Run if field0 is a String, whatever the value of field1")
}
on MyGenericEvent<?, Double> {
info("Run if field1 is a Double, whatever the value of field0")
}
on MyGenericEvent<String, Double> {
info("Run only if field0 is a String and field1 is a Double")
}
on MyGenericEvent<? extends String, Double> {
info("Run only if field0 is a String and field1 is a Double")
}
on MyGenericEvent<Number, Integer> {
info("Run only if field0 is a String and field1 is a Double")
}
}
Basically, when an event MyGenericEvent
is received, it is associated to the definition of concrete types for T1
and T2
.
For example, the event corresponding to new MyGenericEvent<String, Double>
is received, then T1
is assimilated to String
and T2
to Double
.
From this example of event occurrence, the activated event handlers will be because the declared geneic types ar ematching <String, Double>
from the event:
on MyGenericEvent
on MyGenericEvent<?, ?>
on MyGenericEvent<String, ?>
on MyGenericEvent<?, Double>
on MyGenericEvent<String, Double>
on MyGenericEvent<? extends String, Double>
But, on MyGenericEvent<Number, Integer>
is not run because the generic types are not matching those from the event.
An agent is an autonomous entity having a set of skills to realize the capacities it exhibits. An agent has a set of built-in capacities considered essential to respect the commonly accepted competencies of agents, such autonomy, reactivity, pro-activity and social capacities.
Consequently, a behavior associated to an agent is able to use the skills of this agent.
The definition of a capacity or a skill is out of the scope of this reference document. For details, please read the Capacity Reference, and the Skill Reference.
In the rest of this section, it is assumed that the following capacity and skill are defined:
capacity Cap {
def action
}
skill Ski implements Cap {
uses Logging
def action {
info("Action")
}
}
When a behavior must use a capacity, its agent must own an implementation of this capacity: a skill. It is possible for a behavior to assign a skill to its agent.
behavior MyBehavior {
new (owner : Agent) {
super(owner)
var theSkill = new Ski
setSkill( theSkill, Cap )
}
}
If some cases, you may want to set the skill if one was not set up before. The specific behavior
is supported by setSkillIfAbsent(Skill, Class<? extends Capacity>*)
.
behavior MyBehavior {
new (owner : Agent) {
super(owner)
var theSkill = new Ski
setSkillIfAbsent( theSkill, Cap )
}
}
For invoking a function implemented by a skill, the two following steps must be done:
getSkill(Class<? extends Capacity>)
permits retrieving the skill associated to the given capacity;Note This method of invocation is not recommended by the SARL developers. You should prefer the use of the extension methods (see below).
behavior MyBehavior {
on SomeEvent {
// Retreive the capacity implementation
var s = getSkill(Cap)
// Run the action of the skill
s.action
}
}
Invoking a capacity/skill with the getter method is not user-friendly. Since the General Syntax Reference describes the “extension method” mechanism, it is possible to use it for invoking the capacities.
But, instead of using an import
directive, the uses
keyword is provided for importing the
capacities into the agent. In the following example, the Cap
capacity is imported.
After a capacity was “imported”, it is possible to directly call the functions of the capacity
(according to the extension method syntax). In the following example, the action
with the name action
is invoked. This action is defined in the Cap
capacity.
behavior MyBehavior {
uses Cap
on SomeEvent {
// Run the action of the skill
action
}
}
Copyright © 2014-2024 SARL.io, the Original Authors and Main Authors.
Documentation text and medias are licensed under the Creative Common CC-BY-SA-4.0; you may not use this file except in compliance with CC-BY-SA-4.0. You may obtain a copy of CC-BY-4.0.
Examples of SARL code are licensed under the Apache License, Version 2.0; you may not use this file except in compliance with the Apache License. You may obtain a copy of the Apache License.
You are free to reproduce the content of this page on copyleft websites such as Wikipedia.
Generated with the translator docs.generator 0.14.0.