Scalar variables

A scalar variable is similar to the familiar typed programming language variable. The term scalar, in this context, means “has no user visible components” which means that it is not a table or class type. So the type of a scalar variable is not defined in the class model. The type definition system for scalar values is entirely orthogonal to the xUML modeling language.

The familiar unconstrained mathematical types such as Boolean, Integer, Real, Rational and String are available as system types. These are used as a basis for specifying constrained types. Whereas the set of all possible String values of a given maximum length is quite large, the set of all legal ICAO Airport Codes is much more constrained. So a type like ICAO Airport Code could be defined as a constraint on the base type String.

There is no limit to the complexity or structure of an unconstrained type.

Note that a scalar type is defined not just by defining a set of values and a structure. Operations supported by the type must also be defined.

See more about scalar type definition here.

Implicit scalar declaration

Use the = assignment operator to assign and implicitly declare a scalar value.

cabin location = /R1/Cabin.Floor

Here the type defined for the Cabin.Floor attribute establishes the type of the cabin location scalar variable. We know it is a scalar variable because the = assignment operator was used.

But you need to be absolutely sure that the RHS produces a single instance, otherwise a fatal error occurs. If, for example, the relationship to Cabin across R1 is 0..1, an error will can occur.

It is the modeler’s responsibility to take the class model into account when writing action language. In the case of a 0..1 access, it is important to consider both cardinalities. Otherwise, the modeler can refactor the class model to eliminate the 0..1 access via an association class or a generalization. But there are certainly cases where the modeler may wish to retain the 0..1 access.

There are two ways to handle this conditionality in Scrall.

if (/R1/Cabin) {
   x = /R1/Cabin.Floor
   // other actions
} else {
    // no cabin case actions
}

Or:

my cabin .= /R1/Cabin
if (my cabin) {
   // just use my cabin.Floor instead of x
} else {
   // no cabin case
}

Alternatively, guards can be used:

my cabin .= /R1/Cabin
   [exists]: [Cabin assigned] // 
[Cabin assigned] {
   x = my cabin.Floor
}
[!Cabin assigned] {
   // no cabin case
}

Since you can name a guard anything you like, it can lead to more readable actions. You don’t need the usual comments to explain if-else clauses.

You can find a full explanation of errors, guards, conditions and if-then clauses in the appropriate sections of this document.

Another common case of implicit assignment is via input parameters. Here we assume that there is one integer input i, one real input r, and one PSI pressure value.

x = in.r  // x is inferred as real since in.r is a real number
i = in.i // i is integer since in.i holds an integer
c = in.pressure // c is PSI to match the in.pressure data type

Scalar element access

Now consider a Point and a Rectangle data type. ThePointdata type has two components X and Y, each of type Position. The Rectangle data type has components Origin of type Point and Length and Width each of type Distance.

Certain values are accessible by the user using () notation. There is no notion of private/public. It is simply the case that certain components are accessible. Furthermore, those elements may or may not be computed values. From the perspective of action language access there is no distinction.

rects origin = rect( Origin )  // Assigns Origin component

In the example above the Origin component is extracted as a Point data type. This means that the LHS must also be a Point data type. If the LHS variable was previously initialized or implicitly defined as some other data type, there will be an error on this assignment.

x, y = rect( Origin(X, Y) )

Note that there are two levels of extraction. This is necessary so that the X and Y elements are correctly ordered assigned to the x, y variables. The left right order is preserved in the assignment. So, this is also legal:

y, x = rect( Origin(Y, X) )

Each LHS variable in either example will assume the Position data type.

You may have wondered why Scrall doesn’t use the more familiar dot notation for data type component access. Two reasons. As you can see from the above examples it makes it easier to assign multiple components in one line. Also, readability is enhanced. You don’t get lost scanning a trail of dot accesses losing track of which is an attribute and which is a data type component.

Explicit scalar declaration

Any variable not currently in use may be introduced with an explicit data type.

There are a few cases where explicit definition of a type is required. Use the :: symbol to associated a type with a scalar variable. It is required, for example, when you want to assign multiple attribute values. Consider a Point type that has components X and Y, each of type Real.

pt::Point.init(X: in.x, Y: in.y)

In the example above, two input parameters are assigned using the Point data type’s init operation.

When a scalar variable is explicitly declared it must be assigned a complete value. Uninitialized or partially initialized variables will trigger a fatal error.

pt::Point.init(x: in.x, y: in.y)  // Correctly initialized
pt2::Point.init() // Also ok, gets default initial value

Also you can define multiple variables in one line if they are of the same type:

pt1, pt2::Point.init(x: in.x, y: in.y) // both initialized to the same values

In the above example pt1 and pt2 are both initialized to the same value.