Fork me on GitHub

FAQ on the SARL language Syntax

1. General

1.1. What is the list of all the error, warning and information messages that may be generated by the SARL compiler?

We provide a page that lists all the issue messages from the SARL compiler.

Additionally, several run-time errors from the Janus framework are explained here.

2. Literals

2.1. Can I use the same syntax as in Java for number literals?

No.

When a decimal point is written in the literal, the fractional part and the mantissa part must be specify also, even if these parts are equal to zero. Consequently:

3. Packages

3.1. Why can I not put a string in the package name?

It is not allowed to put a SARL keyword, such as agent, in the name of a package.

But, if you prefix with the ^ character the string that corresponds to a keyword, then it is possible to obtain a package name with one of its components equals to a SARL keyword:

4. Type Declarations

4.1. How can I create instances of anonymous classes?

In SARL, the creation of anonymous classes (interface implementation…) must be done with a closure.

Consider the definition of the following interface:

interface MyInterface {
     def myfunction(parameter : Object) : void
}

The on-the-fly definition and instantiation of an instance of this interface, a.k.a. anonymous class definition in the Java community, could be written is SARL with the following closure:

var instance : MyInterface
instance = [ parameter | /* The code of myfunction() */ ]

4.2. Java syntax for anonymous classes is allowed

In SARL, it is recommended tp create anonymous classes (interface implementation…) must be done with a closure (see previous question).

The Java-based syntax for defining an anonymous class’s instance is allowed, but not recommended in the SARL language. It means that the following code is valid:

var instance = new MyInterface() {
	def myfunction(parameter : Object) {
		/* The code of myfunction() */
	}
}

5. Fields and Variables

5.1. Is the ‘val’ keyword defining a constant?

Yes and No.

Indeed, the val keyword defines a name that it could be initialized only once time. It is similar to the final modifier of the Java language.

Consider the example below: two values are defined, a and b. The a variable is a real constant because it has a raw type and it is initialized. The b variable is not a real constant because it is a reference to an object. The reference is constant, but the referred object is not. Consequently, it is still possible to call the setters of b.

val a : int = 4
val b : Object = new Object

5.2. Why can not a static field be defined in an agent type declaration (agent, skill, behavior)?

This is a design choice given that our entities are agents and as such they should not “share” data unless done explicitly in an agent-oriented manner, for example via resources or communication channels. Having static fields in agents or skills would break the “independency” of agents, also known as their autonomy.

It is most probable that such static data can be seen as a resource outside the skill or agent, and as such it should be managed outside it (for example by using the artifact meta-model).

6. Arrays

6.1. Why cannot use the syntax ‘a[0]’ for arrays?

In SARL, the array type may be written with the classic array syntax, such as int[], or the object-oriented syntax, such as List<Integer>.

SARL considers that the each array is a list of something. Consequently, retrieving the values of the array must be done with get(int).

var a : Integer[] = #[1, 2, 3]
var b : List<Integer> = newArrayList(1, 2, 3)

a.get(0) == b.get(0)

7. Generic Types

7.1. Why can I not use the ‘<>’ notation for generic parameters?

In SARL, the empty generic parameter list, written <> is not supported: a generic type expression must be written between them.

For solving this problem, two choices: i) add a type expression between < and >; ii) remove the generic parameter list.

var firstSolution : List<Integer> = new ArrayList<Integer>
var secondSolution : List<Integer> = new ArrayList

8. Operators

8.1. Equality and identity comparison (==, ===, !=, !==) in SARL and checking for null: same as Java?

The mapping of the operator from SARL to Java are:

It is always better to test valid against null with the === or !== operators.

Because the SARL == operator is mapped to the Java equals() function, and the === and !== operators to the Java == and != operators, it is better/safer, and a best practice, to use === and !== when one of the operands is of primitive type, e.g. null, number constants, primitive type variables. These operators are not replaced neither operator_equals nor operator_notEquals within the Java code.

Usually, the SARL compiler generates a warning to push you to use === in place of ==. But with null == value, an ambiguous call error occurs before the warning is generated. In fact, the SARL compiler tries to find an overloading function for the == operator. Since null has not a specific type, the SARL compiler find multiple overloading functions. Check the documentation for details on the overloading mechanism of SARL.

9. Functions

9.1. Ambiguous call to capacity function

When the calling a capacity function, the SARL compiler complains with an “ambiguous call” error. In the code below, the function myfunction is defined in the capacities C1 and C2. The call to myfunction in the agent definition is the place where the error occurs.

capacity C1 {
	def myfunction
	def myfunction2
}
capacity C2 {
	def myfunction
	def myfunction3
}
agent MyAgent {
	uses C1, C2
	on Initialize {
	    myfunction
	    myfunction2
	    myfunction3
	}
}

This error is standard because the functions of the capacities C1 and C2 are implicitly accessible in the scope of the agent definition, see uses keyword definition. The SARL compiler is then unable to determine that is the function to call.

For solving this issue, the developer must explicitly call the correct version of myfunction by getting the capacity. The following code is the correct call to the function if the function in the capacity C1 should be called:

getSkill(C1).myfunction

9.2. How to return two values?

SARL comes with a Pair<A,B> class to build an object for storing two values, nicknamed “key” and “value”. It comes useful when a method has to return two values instead of just one. For example, the following function returns the next floor and direction that an elevator has to serve:

def kb_getNextJob() : Pair<Integer, Double> {
    //...
}

As of Java 8, and as part of JavaFX, Java provides this Pair<A,B> class; check here and here. Note Pairs are different from Map, which can be seen as a collection of Pairs and with a proper key/value semantics.

There exist more advanced implementations of Pair, for example from Apache. See here, here and here.

SARL itself have compact syntax do deal with Pair, by using a -> b to create a Pair object (a,b). There are also compact ways of manipulating Collection and Maps.

Check SARL documentation on that here.

10. Behavior Units - Event Handlers - Guards

10.1. Why is an error or a warning put on the occurrence keyword?

Consider this code:

on CarArrivedPercept {
    cars.get(occurrence.car).floor = occurrence.floor
}

We know that occurrence is static, so cannot be changed. However, in the above code, occurrence.car, is not being changed/assigned, but just used to refer to another entity where assignment is performed. However, SARL compiler will think that occurrence.car may be changed due to a border effect of the get, and complain with warning.

Consider this code:

on CarArrivedPercept {
    occurrence.floor = 1
}

The line occurrence.floor = 1 generates an error because in this case the SARL compiler is sure that the occurrence instance is changed.

In order to avoid the warning above, you could write the code as:

on CarArrivedPercept {
    var c = occurrence.car
    cars.get(c).floor = occurrence.floor
}

10.2. Error “Expression with side effect is not allowed in guards”

The enforcement of no-side-effect in guards was introduced in version 0.9: guard expression must not have side effect.

SARL compiler tries to figure out if the functions used in the behavior guard have side-effect. It does some clever analysis of the name of the method (e.g., getters) and also tries to check if the body has any method that is not pure (i.e., that may have side-effects).

If this analysis does not work, the programmer can mark a method as pure using @Pure annotation, e.g.,:

@Pure
def MT_getEntityState(param : E_MoveRandomly) : int {
	// Do something complex
	return 0
}

Details on the documentation page for function definition.

11. Agent Capacities

11.1. How to restrict the list of agents that will receive an event?

The functions for emitting an event are named emit (for emitting a specific context) and wake (for emitting into the internal context only). These functions are provided by the agent capacities DefaultContextInteractions, ExternalContextAccess or Behaviors These two functions have an optional last argument that is the scoping expression:

def emit(e : Event, scope : (Address) => boolean = null)
def wake(e : Event, scope : (Address) => boolean = null)

This scoping expression is a lambda expression that takes the agent’s address of a candidate for receiving the event, and returns true if the agent with the given address should receive the event.

Let a local variable named [:selectedagentidfield] of type UUID that contains the identifier of an agent that is expected to receive an event of type MyEvent. The following code provides the call to the emit for sending the event only to this selected agent.

emit(new MyEvent) [
	it.ID == selectedAgentID
]

The first argument of the emit is the occurrence of the event to send to the other agents. The second argument is the lambda expression for scoping the receivers. This argument is written according to the externalized form of the lambda expression (between brackets). The lambda expression expression has an argument, named it by default, of type Address. This address is the one of a agent candidate for receiving the event. Then, the expression in the lanmda expression tests if the identifier of the candidate is equal to the identifier of the selected agent, namely selectedAgentID

By adding the scoping lambda expression, only the selected agent will receive the agent.

11.2. How do I control the log-level of the Logging built-in capacity?

Use setLogLevel() of the Logging capacity, as explained here in the API documentation.

You could also control the general configuration of the log level from the options of your SARL Run-time Environment, such as Janus.

12. Documentation

12.1. Can we document SARL code for JavaDoc?

Yes. Since the SARL compiler generates valid Java code including the documentation, you could generate the documentation of your SARL program with the standard javadoc tool applied on the generated Java files.

Additionally, you could use a specific Javadoc doclet in order to generate a documentation that follows the SARL syntax, instead of the Java syntax.

You could find details on the page dedicated to the Maven documentation plugin.

Copyright © 2014-2023 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-SNAPSHOT.