Errors in Chapel

use Error;
use IO;

// By convention, errors are returned as the last value.
proc f(arg: int): (int, Error) {
    if arg == 42 {
        // We can create a new error with a message.
        return (-1, new Error("can't work with 42"));
    }

    // A nil value in the error position indicates that
    // there was no error.
    return (arg + 3, nil);
}

// Sentinel errors are predefined error objects used to
// signify specific error conditions.
var ErrOutOfTea = new Error("no more tea available");
var ErrPower = new Error("can't boil water");

proc makeTea(arg: int): Error {
    if arg == 2 {
        return ErrOutOfTea;
    } else if arg == 4 {
        // We can wrap errors to add context. In Chapel, we can
        // create a new error that includes the previous error's message.
        return new Error("making tea: " + ErrPower.message());
    }
    return nil;
}

proc main() {
    for i in [7, 42] {
        // It's common to use an inline error check.
        var (r, e) = f(i);
        if e != nil {
            writeln("f failed: ", e.message());
        } else {
            writeln("f worked: ", r);
        }
    }

    for i in 0..4 {
        var err = makeTea(i);
        if err != nil {
            // In Chapel, we don't have a built-in way to check if an error
            // is a specific predefined error. Instead, we can compare messages.
            if err.message() == ErrOutOfTea.message() {
                writeln("We should buy new tea!");
            } else if err.message().startsWith("making tea: " + ErrPower.message()) {
                writeln("Now it is dark.");
            } else {
                writeln("unknown error: ", err.message());
            }
            continue;
        }
        writeln("Tea is ready!");
    }
}

In this Chapel translation:

  1. We use the Error module for error handling.

  2. Functions that can return errors have an Error type as their last return value.

  3. We create errors using the new Error() constructor.

  4. Sentinel errors are created as predefined Error objects.

  5. Error wrapping is simulated by creating a new error with a message that includes the original error’s message.

  6. Since Chapel doesn’t have a built-in way to check if an error is a specific predefined error (like Go’s errors.Is), we compare error messages instead.

  7. The nil value is used to indicate no error, similar to Go.

  8. We use Chapel’s writeln function for output instead of fmt.Println.

This translation preserves the error handling concepts from the original Go code, adapting them to Chapel’s error handling mechanisms. The overall structure and flow of the program remain similar, demonstrating error creation, checking, and handling in Chapel.