Skip to main content
Tolk provides flexible constructs for controlling contract flow. Use if, assert, and loops to express conditional logic. The match expression is a powerful “pattern-matching” feature.

if statement

Similar to common languages, optionally followed by else if or else.
if (condition) {
    // ...
} else {
    // ...
}
A condition must be a boolean or an integer (will be true if not equals 0):
if (someInt) {     // means "someInt != 0"
    // ...
}
The body of if and else must be enclosed in { ... }:
// invalid
if (condition)
    someFn();

// valid
if (condition) {
    someFn();
}

assert statement

assert throws an exception if a condition is false.
assert (condition) throw ERR_CODE;
It is equivalent to the following form:
if (!condition) {
    throw ERR_CODE;
}
See exceptions.

match expression

match is used to perform different actions for different values of a variable. One common use case is routing values of a union type:
fun demo(v: A | B | C) {
    match (v) {
        A => {
            // use `v.aField` etc.
        }
        B => { /* ... */ }
        C => { /* ... */ }
    }
}
The match is equivalent to a series of if-else checks:
fun demo(v: A | B | C) {
    if (v is A) {
        // use `v.aField` etc.
    }
    else if (v is B) { /* ... */ }
    else { /* ... */ }
}
The match can also be used for expressions (switch-like behavior):
fun invertNumber(curValue: int) {
    return match (curValue) {
        1 => 0,
        0 => 1,
        else => throw ERR_UNEXPECTED,
    };
}
See union types and pattern matching.

Ternary operator

A ternary condition ? when_true : when_false is also available:
fun myAbs(v: int) {
    return v < 0 ? -v : v
}
Note that if types of when_true and when_false are different, a resulting type is a union. However, this is typically not the intended result, so the compiler reports an error.
fun demo(a: int32, b: int64) {
    // instead of inferring result1 as `int32 | int64`,
    // an error "probably it's not what you expected"
    val result1 = a > b ? a : b;

    // correct: specify the type manually
    val result2: int = a > b ? a : b;

    // also correct, types are compatible
    val result3 = a > b ? a as int64 : b;
}

while loops

while and do-while loops repeatedly execute their bodies while the condition remains true. while checks the condition first and may not execute the body at all, whereas do-while runs the body first.
fun demoWhile(i: int) {
    while (i > 0) {
        debug.print(i);
        i -= 1;
    }
}

fun demoDoWhile() {
    var rand: int;
    do {
        rand = random.uint256();
    } while (rand % 2 == 0);
    return rand;  // an odd number
}
For instance, while is used to iterate over maps:
fun iterateOverMap(m: map<int32, Point>) {
    var r = m.findFirst();
    while (r.isFound) {
        // ...
        r = m.iterateNext(r);
    }
}

repeat loop

The repeat (N) statement executes a block N times:
repeat (5) {
    // executed 5 times
}
N does not have to be a constant; it may also be a variable.

Break and continue in loops

The keywords break and continue are not implemented yet, and a for loop may be added in future versions.