import ballerina/io;

public function main() returns error? {
    do {
        // If either foo() or bar() invocations returns an error,
        // error will be returned from main function and execution
        // of main function ends.
        check foo();
        check bar();
        if !isOK() {
            // explicitly fail with an error.
            fail error("not OK");
        }
    }
    // failure with the respective error is caught by `on fail` block.
    on fail var e {
        io:println(e.toString());
        return e;
    }
}

function foo() returns error? {
    io:println("OK");
}

function bar() returns error? {
    io:println("OK");
}

function isOK() returns boolean {
    // not OK
    return false;
}

Check Semantics

check semantics is not simply to return on error. When check gets an error, it fails. Enclosing block decide how to handle failure. Most blocks pass failure up to enclosing block. Function definition handles failure by returning the error. on fail can catch the error. fail statement is like check but always fails. Differs from exceptions in that control flow is explicit

import ballerina/io;
public function main() returns error? {
    do {
        check foo();
        check bar();
        if !isOK() {

If either foo() or bar() invocations returns an error, error will be returned from main function and execution of main function ends.

            fail error("not OK");
        }
    }

explicitly fail with an error.

    on fail var e {
        io:println(e.toString());
        return e;
    }
}

failure with the respective error is caught by on fail block.

function foo() returns error? {
    io:println("OK");
}
function bar() returns error? {
    io:println("OK");
}
function isOK() returns boolean {
    return false;
}

not OK

bal run check_semantics.bal
OK
OK
error("not OK")
error: not OK {}