Expressions
In Cambrian, most constructs are expressions – they evaluate to a value. This
includes arithmetic, comparisons, if/else, match, and blocks. This
design enables a concise, functional style where the last expression in a block
becomes its value.
Expression Categories
| Category | Operators / Forms | Chapter |
|---|---|---|
| Arithmetic | + - * / % (checked); +% -% *% (wrapping) | Arithmetic and Logic |
| Comparison | ==, !=, <, <=, >, >= | Arithmetic and Logic |
| Logical | &&, ||, ! | Arithmetic and Logic |
| Bitwise | &, |, ^, <<, >> | Arithmetic and Logic |
| Control flow | if/else, match | Control Flow |
| Iteration | for x in iter { body } | See Iteration (for) below |
| Blocks | { let a = ...; expr } | Blocks and Let Bindings |
| Field access | record.field, map[key] | Method Calls and Field Access |
| Method calls | list.len(), map.get(key) | Method Calls and Field Access |
| Type casts | expr as Type | Method Calls and Field Access |
Everything Is an Expression
Unlike imperative languages where if is a statement, in Cambrian if/else
produces a value:
let fee = if amount > 1000 { 10 } else { 5 };
Similarly, match is an expression:
let label = match status {
Status::Active => "active",
_ => "inactive"
};
And blocks evaluate to their last expression:
let result = {
let a = compute_x();
let b = compute_y();
a + b
};
Expressions in Context
Expressions appear in several contexts throughout a Cambrian program:
- Member transforms – the body of a member transform is an expression that produces the new state value:
m_balance: U256 {
in deposit(amount) => m_balance + amount
}
- Where clauses – preconditions are boolean expressions:
where amount > 0 : throw 100
- Route actions – expressions appear in
letbindings, message arguments, and conditional guards:
let fee = amount * 3 / 100;
Transfer(amount - fee) ~> recipient
- Pure functions – the function body is an expression:
pure fn clamp(val: u64, lo: u64, hi: u64) -> u64 {
if val < lo { lo }
else if val > hi { hi }
else { val }
}
Iteration (for)
Expression-level for produces a value (typically a Vec) by mapping
over an iterator:
pure fn double_all(xs: Vec<u32>) -> Vec<u32> {
for x in xs { x * 2 }
}
pure fn first_n(n: u32) -> Vec<u32> {
for i in 0..n { i }
}
Common iterator sources:
| Form | Meaning |
|---|---|
for x in lo..hi { … } | Half-open integer range |
for x in vec_expr { … } | Each element of a Vec |
for (k, v) in map { … } | Key/value pairs from a HashMap |
expr.fold(init, |acc, x| …) | Accumulator loop (scalar, tuple, or record) |
Inside a route action list, a different form sequences side effects per element — no resulting value:
airdrop(amount: U256, recipients: Vec<address>) => [
for r in recipients => [
~> r with { value: amount }
]
]
Restrictions: action-level for cannot nest forbidden effects (V29). On
EVM, supported shapes lower to a single Solidity loop. See
Control Flow for if/match and
Route Actions for effectful loops in routes.