Skip to content
Leon Starr edited this page Nov 6, 2023 · 2 revisions

With data flow semantics we don’t think so much in terms of the programming language style if-then-else. Rather we imagine an action that evaluates an expression and then emits one or more mutually exclusive control outputs. Each output may enable any number of downstream actions.

Single control output

In the simplest case we have the notion of a “guard”. A guard is an enabling condition defined as boolean expression. If the expression evaluates as true, the associated action block executes.

The guard must be placed on the leftmost side of a statement, with the action block to the right as shown:

(altitude > target.Altitude)? look down -> ME

Parentheses enclose the guard so that it is easily distinguished from the enabled action. The leftmost placement along with the suffix ? symbol establishes the expression’s role as a guard.

Named control output

A boolean variable often serves as a more readable guard. This is especially the case when you want to use the same guard multiple times within an activity. First set a boolean scalar variable and then you use it as a guard.

higher = (altitude > target.Altitude) // set guard

// somewhere further down in the activity, use the guard
higher? look down -> ME

Parentheses are not required when you use a single variable as a guard. But they are when you do not!

Two control outputs

To specify two mutually exclusive control outputs, add the : symbol and a second action block after it.

lower? look down-> me : look up-> ME

The choice between two alternate signal actions with the same sender is so common that you can imply both targets are the same by omitting the first one :.

lower? look down -> : look up -> ME

You can use this abbreviation only when the “then” and “else” action each consists of a single signal action. And this is the only case where you may omit the target of an event action.

Two named control outputs

It may be helpful to name both true and false conditions. You can do it like this:

higher!lower = (altitude > target.Altitude)

The first (leftmost) variable is assigned the result of the expression and the second variable is the negation (not) of the first variable. Then you can do this:

higher? {
    // do higher stuff
}

// then after many lines of action language later...

lower? {
    // do lower stuff
}

Of course, you can just make the negation explicit if that reads better:

!higher ? {
    // do lower stuff
}

Multiple named control outputs

Sometimes, though, there are more than two possible conditions to consider:

// Location of floor relative to this elevator cabin

(above, across, below) = (Height <, ==, > dest floor.Height)

above? { // above action block }
across? { // across action block }
below? { // below action block }

Each comparison on the right yields a value assigned to the corresponding variable on the left. The three guards can be used further down.

Finally, you can combine both approaches:

(above, across!away, below) = (Height <, ==, > dest floor.Height)

above? { // above action block }
across? { // across action block }
below? { // below action block }
away? { // not across action block }

Introduction

Model semantics

Flows (as Variables)

Constants and literals

Structure of an activity

Accessing the class model

Data flow


Grammar and parsing notes

Components

Clone this wiki locally