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

Functions and Let Bindings

Functions

Functions are pure — they cannot modify state. Use them for reusable logic.

func LastLogTerm(l) { if len(l) == 0 then 0 else l[len(l) - 1] }
func Quorum(n) { (n / 2) + 1 }
func Max(a, b) { if a > b then a else b }
func Min(a, b) { if a <= b then a else b }

Functions can be called from actions, invariants, guards, and other functions:

action BecomeLeader(i: 0..N) {
    require len({k in 0..N if votesGranted[i][k]}) >= Quorum(N + 1)
    role = role | {i: 2}
}

invariant LogConsistency {
    all i in 0..N: all j in 0..N:
        LastLogTerm(log[i]) == LastLogTerm(log[j])
            implies len(log[i]) == len(log[j])
}

Let bindings

Local definitions within expressions. Two forms:

Expression-level let ... in

let x = len(log[i]) in
    if x == 0 then 0 else log[i][x - 1]

Can be nested:

let a = foo(x) in
let b = bar(y) in
    a + b

In invariants

invariant Safe {
    all i in 0..N:
        let t = currentTerm[i] in
        t >= 0 and t <= MaxTerm
}

In guards

action Act(i: 0..N) {
    let lastIdx = len(log[i]) - 1
    require lastIdx >= 0
    require log[i][lastIdx] == currentTerm[i]
    ...
}

Let bindings improve readability by naming intermediate values and avoiding repeated subexpressions.