Error Handling
Current Approach
Braid v1 does not have try/except or exception handling mechanisms in the parser or runtime. Instead, error handling is done through return value checking, nil returns, and pattern matching. This follows a philosophy similar to Go's explicit error checks.
Nil-Returning Functions
When an operation can fail, the function returns nil to signal failure. The caller checks the result before proceeding.
fn divide_safe(a: float, b: float) {
if b == 0.0 {
return nil;
}
return a / b;
}
fn lookup_user(id: int) {
if id < 0 {
return nil;
}
return User { id: id, name: "user_" + id };
}Checking for Nil
After calling a function that may return nil, use an if check to handle the failure case.
fn read_config(path: string) {
let file = open_file(path);
if file == nil {
print("failed to open config");
return;
}
let data = read_file(file);
print("config: " + data);
}
fn main() {
let result = divide_safe(10.0, 0.0);
if result == nil {
print("division by zero!");
} else {
print("result: " + result);
}
}Pattern Matching on Nil
Use match to handle both success and failure branches cleanly. Thenil pattern matches the null value, and the wildcard _matches success.
fn find_user(id: int) {
if id != 1 {
return nil;
}
return User { name: "Alice", age: 30 };
}
fn main() {
let user = find_user(1);
match user {
nil => print("user not found"),
_ => print("found: " + user.name),
}
}Guard Pattern with Early Returns
A common pattern is to check for invalid inputs or failed operations early in a function and return immediately. This reduces nesting and keeps the happy path clear.
fn process_file(path: string) {
if path == "" {
print("empty path");
return;
}
let file = open_file(path);
if file == nil {
print("could not open: " + path);
return;
}
print("processing " + path);
close_file(file);
}Boolean Flag Returns
For operations that succeed or fail without a value, return a bool flag.
fn validate_email(email: string) -> bool {
if email == "" {
return false;
}
if email.find("@") == -1 {
return false;
}
return true;
}
fn main() {
let valid = validate_email("alice@example.com");
if !valid {
print("invalid email");
} else {
print("email ok");
}
}Status Code Pattern
For operations with multiple failure modes, use an integer status code or enum approach.
enum Status {
OK;
NotFound;
PermissionDenied;
InternalError;
}
fn delete_record(id: int) -> Status {
if id < 0 {
return Status.NotFound;
}
if id == 0 {
return Status.PermissionDenied;
}
return Status.OK;
}
fn main() {
let status = delete_record(0);
match status {
Status.OK => print("deleted"),
Status.NotFound => print("not found"),
Status.PermissionDenied => print("permission denied"),
_ => print("unknown error"),
}
}Future Plans
Future versions of Braid may introduce more structured error handling, including:
try/catchblocks for exception-style handlingResult<T, E>generic type for typed error returns?operator for propagating errors (like Rust or Zig)- Stack traces for debugging error origins