ExpressionsExpression

Expressions

The expression sub-language is a syntactical mixture of Java and OCL. This documentation provides a detailed description of each available expression. Let us start with some simple examples.

Accessing a property:

myModelElement.name

Accessing an operation:

myModelElement.doStuff()

simple arithmetic:

1 + 1 * 2

boolean expressions (just an example:-)):

!('text'.startsWith('t') && ! false)

There are several literals for built-in types:

Like OCL, the Xpand expression sub-language defines several special operations on collections. However, those operations are not members of the type system, therefore you cannot use them in a reflective manner.

There are two different forms of conditional expressions. The first one is the so-called if expression . Syntax:

condition ? thenExpression : elseExpression

Example:

name != null ? name : 'unknown'

Alternatively, you also could write:

      if name != null then
         name 
      else
        'unknown'
    

The other one is called switch expression . Syntax:

switch (expression) {
   (case expression : thenExpression)*
   default : catchAllExpression
}

The default part is mandatory, because switch is an expression, therefore it needs to evaluate to something in any case. Example:

switch (person.name) {
   case 'Hansen' : 'Du kanns platt schnacken'
   default : 'Du kanns mi nech verstohn!'
}

There is an abbreviation for Boolean expressions:

switch {
   case booleanExpression : thenExpression
   default : catchAllExpression
} 

Expressions and functional languages should be free of side effects as far as possible. But sometimes there you need invocations that do have side effects. In some cases expressions even don not have a return type (i.e. the return type is Void). If you need to call such operations, you can use the chain expression. Syntax:

anExpr ->
anotherExpr ->
lastExpr 

Each expression is evaluated in sequence, but only the result of the last expression is returned. Example:

pers.setName('test') ->
pers

This chain expression will set the name of the person first, before it returns the person object itself.

The create expression is used to instantiate new objects of a given type:

new TypeName

The let expression lets you define local variables. Syntax is as follows:

let v = expression : expression-with-v 

This is especially useful together with a chain- and a create expressions. Example:

let p = new Person :
   p.name('John Doe') ->
   p.age(42) ->
   p.city('New York') ->
   p

Sometimes you don't want to pass everything down the call stack by parameter. Therefore, we have the GLOBALVAR expression. There are two things you need to do, to use global variables.

The expressions language supports multiple dispatching . This means that when there is a bunch of overloaded operations, the decision which operation has to be resolved is based on the dynamic type of all parameters (the implicit 'this' included).

In Java only the dynamic type of the 'this' element is considered, for parameters the static type is used. (this is called single dispatch)

Here is a Java example:

class MyClass {
   boolean equals(Object o) {
      if (o instanceof MyClass) {
         return equals((MyClass)o);
      }
      return super.equals(o);
   }
   boolean equals(MyType mt) {
      //implementation...
   }
} 

The method equals(Object o) would not have to be overwritten, if Java would support multiple dispatch.

The expression language is statically type checked. Although there are many concepts that help the programmer to have really good static type information, sometimes. one knows more about the real type than the system. To explicitly give the system such an information casts are available. Casts are 100% static, so you do not need them, if you never statically typecheck your expressions!

The syntax for casts is very Java-like:

((String)unTypedList.get(0)).toUpperCase()


[1] since 4.1.2