Alpha. Vary is under active development and not ready for production use. Syntax, APIs, performance, and behaviour may change between releases.

Error handling

Functions signal problems by raising errors, and callers catch them with try/except blocks. When you want errors as values instead of exceptions, there is also Result[T, E] with Ok and Err variants.

Try / except / finally

def divide(a: Int, b: Int) -> Int {
    if b == 0 {
        raise ValueError("division by zero")
    }
    return a // b
}

try {
    let result = divide(10, 0)
} except ValueError as e {
    print("Bad value")
} except Error as e {
    print("Some other error")
} finally {
    print("Always runs")
}

except Error: is the broadest catch. Bare except: is not supported. You can raise a string directly: raise "message" wraps it in RuntimeError.

Error types

TypeUse case
ErrorBase of all Vary errors
RuntimeErrorGeneral runtime errors
TypeErrorType mismatches
ValueErrorInvalid values
IOErrorI/O failures
KeyErrorMissing dictionary keys
IndexErrorOut-of-bounds access
ContractViolationFailed precondition or postcondition
ProcessErrorSubprocess execution failure (not found, permission denied, timeout)
TimeoutErrorTask group exceeded its timeout

Result type

Result[T, E] represents success or failure without exceptions:

def divide(a: Int, b: Int) -> Result[Int, Str] {
    if b == 0 {
        return Err("division by zero")
    }
    return Ok(a // b)
}

let r = divide(10, 3)
assert r.is_ok()
assert r.unwrap() == 3
MethodReturnsDescription
.is_ok()BoolTrue if Ok
.is_err()BoolTrue if Err
.unwrap()TUnwrap Ok value (panics on Err)
.unwrap_err()EUnwrap Err value (panics on Ok)
.unwrap_or(default)TUnwrap Ok, or return default

Match on Result with case Ok(value) and case Err(error).

The ? operator

The ? postfix operator unwraps Ok or returns Err early:

def process() -> Result[None, FsError] {
    let wp = WritePath.of("/tmp/out.txt")?
    let _w = fs.write_text(wp, "hello")?
    return Ok(None)
}

The enclosing function must return a Result with the same error type.

The ?else return operator

When unwrapping a Result or optional, ?else return provides a default value and exits the function early if the unwrap fails:

def get_name(id: Int) -> Str {
    let user = find_user(id) ?else return "unknown"
    return user.name
}

This is useful when the enclosing function does not return a Result and you want a fallback instead of propagating the error.