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

Members

Members are persistent state variables owned by an entity. Unlike traditional programming where state is mutated imperatively, Cambrian members declare how they change in response to routes. Each member defines its own transformation logic – this is the member-centric state model.

Declaring Members

Members are declared directly in the entity body, at the same level as routes { } — there is no separate members { } wrapper in the grammar:

m_owner: address {
    in setup(owner) => owner
}

m_balance: U256 {
    in deposit(amount) => m_balance + amount
    in withdraw(amount) => m_balance - amount
}

m_count: u64 {
    in increment() => m_count + 1
    in reset() => 0
}

Member Declaration

Each member has a name, a type, and one or more transforms:

m_name: Type {
    in route_name(params) => new_value_expression
    in another_route(params) => another_expression
}
PartDescription
Namem_ prefix followed by snake_case
TypeAny Cambrian type
TransformsOne in clause per route that modifies this member

The m_ Convention

Member names use the m_ prefix by convention. This distinguishes state variables from local bindings, parameters, and other names. While the compiler does not enforce the prefix, it is universally used in Cambrian code.

Reading Members

Member values are accessible by name in:

  • Where clauses: where m_balance >= amount : throw 100
  • Route actions: ~> m_owner with { value: m_balance }
  • Macros: macro is_owner() -> bool = { msg::sender == m_owner }
  • Other member transforms: in transfer(to, amount) => m_balance - amount

When read in a route’s action list or where clause, the value is the pre-transform value (the value before the current route’s transforms are applied).

Transform Basics

Transforms define the new value of a member after a route executes. The transform body is an expression:

m_count: u64 {
    in increment() => m_count + 1
}

This says: “When the increment route fires, the new value of m_count is the current value plus one.”

See State Transforms for detailed coverage.

Members Without Transforms

If a member does not list a transform for a route, it remains unchanged when that route fires. Only members that explicitly declare in route_name(...) are affected by that route.

Temporal References

Within a single route execution, you may need to reference the post-transform value of a member. The ^ prefix provides this:

m_count: u64 {
    in increment() => m_count + 1
}

m_count_doubled: u64 {
    in increment() => ^m_count * 2
}

Here ^m_count refers to the value of m_count after its transform in the increment route (i.e., m_count + 1).

See Temporal References for details.

Member Types

Members can be of any type:

TypeExample Use
u64, U256Counters, balances, amounts
boolFlags, status indicators
addressOwner, treasury, linked entities
StringNames, labels
HashMap<K, V>Mappings (balances, approvals)
Vec<T>Lists (participants, history)
Option<T>Optional configuration
RecordsGrouped state (deal details, config)
EnumsState machines (status, phase)

Complete Example

entity Token {
    routes {
        init deploy(name: String, symbol: String, owner: address) => []
        mint(to: address, amount: U256)
            where msg::sender == m_owner : throw 100
        => []
        transfer(to: address, amount: U256)
            where m_balances[msg::sender] >= amount : throw 101
        => []
    }

    m_name: String {
        in deploy(name, _, _) => name
    }

    m_symbol: String {
        in deploy(_, symbol, _) => symbol
    }

    m_owner: address {
        in deploy(_, _, owner) => owner
    }

    m_total_supply: U256 {
        in deploy(_, _, _) => 0
        in mint(_, amount) => m_total_supply + amount
    }

    m_balances: HashMap<address, U256> {
        in mint(to, amount) => {
            let current = m_balances.get(to).unwrap_or(0);
            m_balances.set(to, current + amount)
        }
        in transfer(to, amount) => {
            let from = msg::sender;
            let from_bal = m_balances[from];
            let to_bal = m_balances.get(to).unwrap_or(0);
            m_balances
                .set(from, from_bal - amount)
                .set(to, to_bal + amount)
        }
    }
}