← Back to AILP Home

Contextual catch

v0.33.5 added contextual Result catch without turning catch into a hard keyword.

result_expr catch (err) { handler_expr }

Value flow

For Result<T>:

Input Output
success unwrap and yield the success value T; handler is skipped
error bind err:int32, evaluate handler, yield handler result

The handler expression must also yield T.

func:bad = int32() {
    fail 7i32;
};

func:main = int32() {
    int32:x = bad() catch (err) { err + 30i32 };
    exit x;       // 37
};

The binding is scoped

The error name exists only inside the handler body.

int32:x = bad() catch (code) { code + 1i32 };

The binding type is int32, matching the currently shipped .error / fail lowering path. Earlier design sketches mentioned tbb8; v0.33.5 documented the implementation reality instead of pretending otherwise.

catch is still an identifier

This is valid:

func:main = int32() {
    int32:catch = 7i32;
    exit catch;
};

Only the exact postfix shape result_expr catch (err) { handler } is special. C-style try { ... } catch { ... } is still rejected.

Using ? inside a handler

The handler is just an expression. Other Result handlers keep their normal meaning inside it.

int32:x = bad() catch (err) { also_bad() ? (err + 30i32) };

Here the inner ? fallback can refer to the catch binding.

Not exceptions

catch is Result deconstruction/fallback sugar. It does not unwind the stack, catch thrown exceptions, or change pass / fail control flow.