Control flow
AEL, the Agent Engineering Language, has four ways to direct control: if, while, match and return. Each is a statement, and the check makes sure that every path through a function ends the way its type says.
if and else
fn shipping(weight: u32, express: bool) -> u32 {
if express {
return 20;
} else {
if weight > 1_000 {
return 12;
}
}
return 5;
}
- The condition must be a
bool. An integer, text orOptionis never a condition. - The condition needs no parentheses; the blocks always need braces.
elsetakes a block. To test a further condition, put anifinside that block, as above.- Only the chosen block runs.
while
fn sum_to(limit: u64) -> u64 {
let mut total: u64 = 0;
let mut n: u64 = 1;
while n <= limit {
total = total + n;
n = n + 1;
}
return total;
}
- The condition is checked before every pass, including the first, so the body may run zero times.
- There is no
breakorcontinue. End a loop through its condition, or leave the function withreturn.
match
match compares one value against patterns, in order, and runs the block of the first that fits.
enum Reply {
Empty,
Text(Str<200>),
Code(u16, Str<40>),
}
fn status(reply: Reply) -> u16 {
match reply {
Reply::Empty => { return 204; }
Reply::Code(code, _) => { return code; }
_ => { return 200; }
}
}
- The value is evaluated once.
- The patterns must cover every possible value, and none may repeat another. A pattern after
_can never be reached, so it is refused. - An enum needs every variant, or
_. Aboolneedstrueandfalse, or_. Integer patterns, such as0or-1, need a final_. - You can bind a variant's values to names, as
codeabove, or skip them with_. Binding a value moves it out of the matched value; see Ownership. - Each arm is a block. Its names are visible only inside it.
- Patterns are the wildcard,
trueandfalse, integers and enum variants. Structs, text, lists and references are inspected with ordinary code instead, and a pattern cannot carry an extra condition.
Option and Result are enums, so match is how you handle them:
fn port_or_default(configured: Option<u16>) -> u16 {
match configured {
Option::Some(port) => { return port; }
Option::None => { return 8080; }
}
}
return
return value;leaves the function with a value of exactly its result type.- A function that returns unit can write
return;, or simply reach the end of its body. - Every path through a function with a result must end in a
return. The check counts awhileloop as possibly running zero times, so areturnthat appears only inside a loop is not enough: add one after the loop.
Statements, not expressions
if and match do not produce values, and a block does not produce the value of its last line. To choose a value, give a binding its value in every branch:
fn fee(member: bool) -> u32 {
let amount: u32;
if member {
amount = 0;
} else {
amount = 3;
}
return amount;
}
The check makes sure that amount has a value on every path before it is read. Ownership describes the rule.
Loops and waiting
On a microcontroller, one handler runs at a time, until it waits for something. A loop that never waits keeps every other handler waiting, so keep the work between waits bounded. Concurrency describes scheduling.