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

Pure Functions

Pure functions are top-level, stateless computations declared outside any entity. They can be called from any entity in the same file and are guaranteed to have no side effects – their output depends only on their inputs.

Syntax

pure fn name(param1: Type1, param2: Type2) -> ReturnType {
    body_expression
}

The pure fn keywords introduce the function. The body is a single expression (which can be a block) that produces the return value.

Basic Examples

pure fn max(a: u64, b: u64) -> u64 {
    if a >= b { a } else { b }
}

pure fn min(a: u64, b: u64) -> u64 {
    if a <= b { a } else { b }
}

pure fn clamp(val: u64, lo: u64, hi: u64) -> u64 {
    if val < lo { lo }
    else if val > hi { hi }
    else { val }
}

Restrictions

Pure functions have strict constraints:

AllowedForbidden
ParametersRead member values
Arithmetic, logicWrite member values
if/else, matchSend messages
let bindingsAccess msg:: or sys::
Call other pure functionsSide effects (~>, deploy, emit, evm::)
Type castsCall macros

These constraints ensure that pure functions are deterministic, testable, and free of dependencies on entity state.

Block Bodies

Complex pure functions use block expressions with let bindings:

pure fn compute_fee(amount: U256, rate_bps: u64) -> U256 {
    let rate = rate_bps as U256;
    let fee = amount * rate / 10000;
    fee
}
pure fn weighted_average(a: u64, b: u64, weight_a: u64, weight_b: u64) -> u64 {
    let total_weight = weight_a + weight_b;
    let weighted_sum = a * weight_a + b * weight_b;
    weighted_sum / total_weight
}

Calling Pure Functions

Pure functions are called by name from any expression context:

In Route Actions

routes {
    swap(amount_in: U256) => [
        let fee = compute_fee(amount_in, 30);
        let net = amount_in - fee;
        Transfer(net) ~> m_recipient
    ]
}

In Member Transforms

m_fee_collected: U256 {
    in trade(amount) => m_fee_collected + compute_fee(amount, 30)
}

In Where Clauses

routes {
    trade(amount: U256)
        where amount >= min_trade_amount(m_tier) : throw 100
    => []
}

In Other Pure Functions

pure fn abs_diff(a: u64, b: u64) -> u64 {
    if a >= b { a - b } else { b - a }
}

pure fn is_close(a: u64, b: u64, tolerance: u64) -> bool {
    abs_diff(a, b) <= tolerance
}

Pure Functions vs. Macros

FeaturePure FunctionMacro
Declarationpure fn name(...) -> T { ... }macro name(...) -> T = { ... }
LocationTop-level (outside entities)Inside entity
State accessNoneCan read members
ScopeAll entities in the fileEnclosing entity only
Call syntaxname(args)@name(args)

Use pure functions for reusable logic that does not depend on state. Use macros when you need to combine state access with helper logic. See Macros.

Pure Functions vs. Pure Routes

FeaturePure FunctionPure Route
Declarationpure fn name(...) -> T { ... }pure name(...) -> T => [return(...)]
Callable externallyNoYes (via message)
Internal callsYes, from any expressionAs a route
Use caseInternal computationOn-chain utility API

Complete Example

pure fn compute_output(
    amount_in: U256,
    reserve_in: U256,
    reserve_out: U256,
    fee_bps: u64
) -> U256 {
    let fee = compute_fee(amount_in, fee_bps);
    let effective_in = amount_in - fee;
    effective_in * reserve_out / (reserve_in + effective_in)
}

pure fn compute_fee(amount: U256, bps: u64) -> U256 {
    amount * (bps as U256) / 10000
}

entity DEX {
    routes {
        swap(amount_in: U256, min_out: U256)
            where amount_in > 0 : throw 100
        => [
            let out = compute_output(
                amount_in, m_reserve_a, m_reserve_b, 30
            );
            if out < min_out => [
                // slippage protection
            ] else [
                Token::transfer(msg::sender, out) ~> m_token_b
            ]
        ]
    }
}