14. Error Handling #
14.1 The Result Type #
CRITICAL: All functions that can fail MUST return a Result
type. There are no exceptions, panics, or nulls. This is a core design principle of the language to ensure safety and eliminate entire classes of runtime errors.
The Result
type is a generic union type with two variants:
Success { value: T }
: Represents a successful result, containing the value of typeT
.Error { message: E }
: Represents an error, containing an error message or object of typeE
.
Example:
type Result<T, E> = Success { value: T } | Error { message: E }
The compiler MUST enforce that Result
types are always handled with a match
expression, preventing direct access to the underlying value and ensuring that all possible outcomes are considered.
let result = someFunctionThatCanFail()
match result {
Success { value } => print("Success: ${value}")
Error { message } => print("Error: ${message}")
}
This approach guarantees that error handling is explicit, robust, and checked at compile time.
14.2 Compound Expression Result Propagation #
π¨ CRITICAL DESIGN PRINCIPLE π¨: When multiple arithmetic operations are combined in a single expression, the entire expression returns a single Result
type, not each individual operation.
THE GOLDEN RULE: #
let a = 1 + 3 // β
a: Result<int, MathError>
let a = 1 + 3 + (300 / 5) // β
a: Result<int, MathError> (WHOLE EXPRESSION)
// Individual operations inside are AUTOMATICALLY PROPAGATED
Individual Operations (Each Returns Result) #
let a = 1 + 3 // a: Result<int, MathError>
let b = 300 / 5 // b: Result<int, MathError>
let c = 2 * 4 // c: Result<int, MathError>
Compound Expressions (Single Result, Auto-Propagation) #
let result = 1 + 3 + (300 / 5) // result: Result<int, MathError>
let complex = (a * b) + (c / d) - 10 // complex: Result<int, MathError>
let nested = ((x + y) * z) / (a - b) // nested: Result<int, MathError>
let mega = 1 + 2 * 3 - 4 / 2 + 5 // mega: Result<int, MathError>
WHAT THIS MEANS: #
- β NO: You donβt handle Results for each
+
,-
,*
,/
inside an expression - β YES: You handle the Result ONCE for the entire compound expression
- β‘ AUTO: If any operation fails β whole expression fails
- β‘ AUTO: If all operations succeed β expression returns Success with final value
Error Propagation Rules #
- Any operation fails β Entire expression fails
- All operations succeed β Expression returns Success with final value
- Individual operations inside compound expressions donβt need explicit Result handling
Example Behavior #
// If any operation overflows, the whole expression fails
let calculation = 1000000 * 1000000 + 50 / 2 // Result<int, MathError>
match calculation {
Success { value } => print("Final result: ${value}")
Error { message } => print("Calculation failed: ${message}")
}
Rationale #
This design provides:
- Ergonomic code: No need to unwrap Results for every operation
- Safety: All potential arithmetic errors are still caught
- Clarity: Single error handling point for compound expressions
- Performance: Runtime can optimize arithmetic chains
KEY INSIGHT: You handle the Result once for the entire expression, not for each individual operation within it.