Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

CategoryOperators / FormsChapter
Arithmetic+ - * / % (checked); +% -% *% (wrapping)Arithmetic and Logic
Comparison==, !=, <, <=, >, >=Arithmetic and Logic
Logical&&, ||, !Arithmetic and Logic
Bitwise&, |, ^, <<, >>Arithmetic and Logic
Control flowif/else, matchControl Flow
Iterationfor x in iter { body }See Iteration (for) below
Blocks{ let a = ...; expr }Blocks and Let Bindings
Field accessrecord.field, map[key]Method Calls and Field Access
Method callslist.len(), map.exists(key)Method Calls and Field Access
Type castsexpr as TypeMethod 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 let bindings, 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:

FormMeaning
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.