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.exists(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"
};
if and match (and record literals) are written as a let value, as the
body of a block, pure fn, or closure, or as a braced member-transform body
(in r(x) => { if … }). They cannot be nested directly inside another
expression or call such as return(if …); bind the result with let first.
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 } }
}
There is no else if; nest an if inside the else block instead.
Iteration (for)
Expression-level for produces a value (a Vec) by mapping over an
iterator. It is written in a block or pure fn body (wrap it in { } to use
it as a route let value):
pure fn double_all(xs: Vec<u32>) -> Vec<u32> {
for x in xs { x * 2 }
}
pure fn total(xs: Vec<u64>) -> u64 {
xs.fold(0, |acc, x| acc + x)
}
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.