-
Notifications
You must be signed in to change notification settings - Fork 0
Guards
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.
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.
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!
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.
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
}
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 }
Copyright 2020, 2021, 2022, 2023, 2025 © Leon Starr under MIT Open Source License
- Why they are problematic
- Instance attribute creation values
- Boolean values
- Special values
- Enumerated values
- Action block
- Statement
- Single line action
- Multiple dependent actions on a single line
- An action spread across multiple lines
- A conditional group of single line actions
- Comments
- Finding instances
- Attribute access
- Creation and deletion
- Subclass migration
- Creating a table from a class
- Creating a table with a definition
- Converting a table into a class
- Set operations on tables
- Set comparisons on tables
- Join
- Rename
- Extend
- Aggregation
- Rank
- Image
- Input values
- Signatures and name doubling
- Output values
- Execution order
- Sequential execution
- Conditional execution
- Signals
- Scrall has no for_each action
- Iteration