This document describes how to define Skills in SARL. Before reading this document, we recommend that you read the General Syntax Reference, and the Capacity Reference.
An Action is code (a public method or function) that transforms a part of the designed system or its environment. This transformation guarantees resulting properties if the system before the transformation satisfies a set of constraints. An Action is defined in terms of pre- and post-conditions.
A Capacity is the specification of a collection of Actions. This specification makes no assumptions about the implementation of each Action. It is used to specify what an Agent can do, what behavior is required for its execution.
A Skill is a collection of Actions implementing a Capacity as described in this specification.
A Skill implements a Capacity and is defined with the skill
keyword. This relationship is specified with the implements
keyword.
Below, a Skill is defined to output messages on the standard console (defined in the Capacity Reference). Note that all the Actions defined in the Capacity must have a definition (with a body containing code) in the Skill.
skill ConsoleLogging implements Logging {
def info(text : String) {
System.out.println(text)
}
def debug(text : String) {
System.err.println(text)
}
}
Often it is useful or necessary to base a Skill (a Capacity’s implementation) on attributes (properties or fields).
The following example defines a Skill that uses the standard Java logging library. To avoid creating an instance of the Java logger each time the Capacity’s Actions are invoked, an instance of the Java logger is created and stored in a field of the Skill.
skill StandardJavaLogging implements Logging {
// A field is defined in the Skill
val logger = Logger.anonymousLogger
def info(text : String) {
logger.info(text)
}
def debug(text : String) {
logger.fine(text)
}
}
It is possible to declare methods in the Skill in addition to those specified by the Capacity.
skill MyLogging implements Logging {
def info(text : String) {
output(text)
}
def debug(text : String) {
output(text)
}
// Define an utility function
// that is outputting the text
def output(t : String) {
System.err.println(t)
}
}
Several elements of the skill can be used only after the skill is attached to its owning agent.
For example, the value returned by the function getOwner
is not null
only when the skill is
attached to an agent, i.e. its owner.
In order to enable the developer to write a code that is run when the skill is attached, the function
install
could be defined and implemented. The code below provides an example in which the value
returned by getOwner
is checked.
def install {
// Initialization of the skill
assert getOwner !== null
}
In a similar way as install
, it is possible to execute a code when the skill is detached from it owning agent.
For this purpose, the uninstall
function should be defined, as illustrated below:
def uninstall {
// Do uninstallation statements
}
It is not necessary to specify a constructor for Skills unless a value will be initialized.
Two constructors are defined in the abstract Skill
class:
class Skill extends AgentTrait
implements IBehaviorGuardEvaluatorReceiver {
def getReferenceCount : int
}
Example of constructor definition:
// The constructor is mandatory
// for defining the field "logger"
new (l : Logger) {
super() // Call the super's constructor
logger = l
}
If no constructor is defined in the skill type and a super-type is declared, implicit constructors will be assumed. Implicit constructors has the same prototypes as the constructors of the super type. Details on implicit constructors are given in the reference documentation related to the synthetic functions.
In some situations it is useful to combine more than one capacity in a skill.
Below, the MyLogging
skill is defined as an implementation of the capacities
Logging
and LogReader
.
All the Actions defined in a Capacity must have an implementation in the related Skill.
If two implemented Capacities include the same Action signature, it must be implemented only once in the Skill.
capacity LogReader {
def open(filename : String) : int
def info(t : String)
def close(fid : int)
}
skill MyLogging implements Logging, LogReader {
// Shared implementation for the methods
// defind in the two Capacities.
def info(text : String) {
System.out.println(text)
}
def debug(text : String) {
System.out.println(text)
}
def open(filename : String) : int {
return 0
}
def close(fid : int) {
}
}
In some situations it is useful to specialize the definition of a Skill. This mechanism is supported by the inheritance feature of SARL, which has the same semantics as the inheritance mechanism of the Java object-oriented language.
The extended Skill is specified just after the extends
keyword.
Very Important Note A Skill type can extend only one other Skill type. This is similar to the constraint on the extension of classes in the Java language.
In the following code, the StandardJavaLogging
Skill (defined above) is extended to override the info output.
skill ExtendedLogging extends StandardJavaLogging {
def info(text : String) {
super.info("INFO: "+text)
}
}
Modifiers are used to modify declarations of types and type members. This section introduces the modifiers for the Skill. The modifiers are usually written before the keyword for defining the Skill.
The complete description of the modifiers’ semantic is available in this section.
A Skill may be declared with one or more modifiers, which affect its runtime behavior:
public
: the behavior is accessible from any other type (default);package
: the behavior is accessible only from types in the same package.abstract
: this Skill is abstract and cannot be instantiated; an extension Skill must implement this behavior.final
: an extending Skill may not override this behavior.Examples:
public skill Example1 implements CapacityExample {
}
package skill Example2 implements CapacityExample {
}
abstract skill Example3 implements CapacityExample {
}
final skill Example4 implements CapacityExample {
}
The modifiers for the fields in a Skill are:
public
: the field is accessible from everywhere;protected
: the field is accessible within the same package, and in derived Agents;package
: the field is accessible only within the same package as 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 example1 : Object
protected var example2 : Object
package var example3 : Object
private var example4 : Object
static var example5 : Object
The modifiers for the methods in a Skill are:
public
: there are no restrictions on accessing the method (public);protected
: the method is accessible within the same package, and derived classes;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 example1 { }
// Protected access function
protected def example2 { }
// Package access function
package def example3 { }
// Private access function
private def example4 { }
// Abstract function
abstract def example5
// Not-overridable function
final def example6 { }
// Dispatch functions
dispatch def example7(p : Integer) { }
dispatch def example7(p : Float) { }
// Static / Class function
static def example8 { }
The Skill statement permits specifying a subset of the agent’s behavior units inside the skill to make it able to react to the receiving of some event.
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
.
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
}
skill MySkill implements MyCapacity {
uses Logging
def oneFunction {}
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.
Several Capacities are defined and reserved by the SARL Core Specification. The corresponding Skills are provided by the runtime environment (such as the Janus platform). The built-in Skills are described in the Built-in Capacity Reference.
Details on the use of Skills may be found in the following:
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.