beps/docs/proposals/BEP-001-exceptions/legacy-ignore/updates/inline-catch.md
.catch() Was RemovedThe original proposal included an inline catch syntax:
let x = Foo().catch({
MyError() => { fallback() }
})
This syntax creates semantic confusion about the nature of errors in BAML.
The .catch() syntax implies that errors are values that can be operated on with the . (record access) operator. This creates a conceptual problem:
function A() -> int {
let x = SomethingThatThrows(); // Is x an error value here?
let y = x.catch({}); // If so, why can we call .catch on it?
y
}
In reality, errors in BAML are runtime events that occur during statement execution, not values that get assigned to variables.
Consider this progression:
let x = Foo(); is a statement.Foo() is an expression.Foo().catch({}) implies that .catch is being called on some value.The . operator binds more tightly than whitespace, meaning catch appears to be a method/field on the result of Foo(). This creates the illusion that errors are values.
To make .catch() work correctly, we'd need complex rules like:
"The meaning of
.catch()when applied to a function means that the function changes into a form that will run the catch handler if evaluating that function fails."
Rules like this tend to have bad interactions with other language features and make the semantics harder to understand.
let x = Thrower() catch { ... }; // catch is part of the statement, not the expression
This avoids the . operator problem but introduces its own confusion about operator precedence and statement/expression boundaries.
The simplest and most consistent solution is to use expression blocks, which are already part of BAML's syntax:
function A() -> int {
let x = {
catch { ... }
Thrower()
};
}
Why this works:
catch is clearly the first statement in its scope (the block).catch semantics.We removed .catch() from the proposal and recommend using expression blocks for call-site error handling:
// Instead of:
let x = Foo().catch({ MyError() => { fallback() } })
// Use:
let x = {
catch { MyError() => { fallback() } }
Foo()
}
This maintains semantic clarity while still allowing localized error handling when needed.