Error handling
Use typed errors for expected failures that callers may propagate, inspect, or recover from. Errors are values in a declared set; they are separate from host exceptions and unrecoverable runtime traps.
Declare an error set
Section titled “Declare an error set”An errortype is non-generic and uses datatype-like constructors:
errortype LookupError MissingValue InvalidKey(String)endConstructors belong to their declaring error type, such as
LookupError::InvalidKey("name").
throws changes the caller-visible result
Section titled “throws changes the caller-visible result”errortype LookupError MissingValue InvalidKey(String)end
func require_value(option : Option[Int]) throws LookupError -> Int do switch option case Option::Some(value) then value case Option::None then throw LookupError::MissingValue endendInside the body, the successful result is still Int. To callers, the
function returns the standard library’s canonical Result[Int, LookupError]:
normal completion becomes Result::Ok, while throw exits with
Result::Err.
Propagate with postfix !
Section titled “Propagate with postfix !”errortype LookupError MissingValue InvalidKey(String)end
func require_value(option : Option[Int]) throws LookupError -> Int do switch option case Option::Some(value) then value case Option::None then throw LookupError::MissingValue endend
func double_value(option : Option[Int]) throws LookupError -> Int do require_value(option)! * 2endFor Result[T, E], postfix ! extracts T from Ok. An Err returns early
from the directly enclosing function. That function must declare a compatible
error set.
The operand is evaluated once. Propagation does not catch host exceptions or convert unrelated error values.
Handle a result explicitly
Section titled “Handle a result explicitly”errortype LookupError MissingValue InvalidKey(String)end
func require_value(option : Option[Int]) throws LookupError -> Int do switch option case Option::Some(value) then value case Option::None then throw LookupError::MissingValue endend
func double_value(option : Option[Int]) throws LookupError -> Int do require_value(option)! * 2end
func main() -> Unit do result = double_value(Option::None); switch result case Result::Ok(value) then Debug.trace(value) case Result::Err(LookupError::MissingValue) then Console.println("No value was provided") case Result::Err(LookupError::InvalidKey(key)) then Console.println("Invalid key: " ++ key) end;endHandle the result at the layer that owns recovery or presentation. Use !
when the current function should preserve the same failure contract.
Extend an error set
Section titled “Extend an error set”An error type may include all constructors of one or more base error types:
errortype StorageError Unavailableend
errortype ServiceError extends StorageError InvalidRequestendA StorageError value widens to ServiceError without a wrapper. Inherited
constructors remain qualified by their declaring owner, so code constructs
and matches StorageError::Unavailable, not
ServiceError::Unavailable.
Tests infer their error boundary
Section titled “Tests infer their error boundary”A test declaration has no explicit throws clause. The compiler collects
errors propagated or thrown directly by the test body and builds a private
test-only envelope. That is why both application errors and
Testing.assert_equal(...)! can leave a test naturally.
Option and Result are standard-library types; their constructors and
operators are covered in Option and Result.
Next: Asynchronous code.