Error Handling
This proposal has been accepted in principle.
It is currently under active development.
Parts might be incomplete or missing in Zirric.
Introduction
This proposal introduces a standard error protocol and a Result union type for
failure-aware APIs. It also defines syntactic sugar for extracting values or
fallbacks from results.
Motivation
Zirric currently lacks a canonical way to represent failures. A shared Result model makes error flows explicit and composable.
The usage of results should be ergonomic, minimizing boilerplate when extracting values or providing fallbacks.
Proposed Solution
First of all there will be a @Error attribute. This attribute can be enforced.
Introduce an @AnyResult attribute to be defined on unions.
Then there will be a standard Result union with Ok and Err variants.
@Returns(String!) // Syntactic sugar for @Type(Result) @OkType(String)
fn readFile(path) {
// returns Ok(String) or Err(Error)
}
let text = "Contents:\n" + readFile("notes.txt")!.value // @Type(Result) @OkType(String)
switch text {
case Ok(value):
// use value
case Err(error):
panic(error.debug(error))
}
Syntactic Sugar
result!.valueunwrapsResultintoOk(value)orErr(error).result !! "default"unwraps toOk.valueor returns the default forErr.@String!expands to@Type(Result) @OkType(String).
Detailed Design
@Errormarks types that represent errors and must provide a bounddebug
function returning a string.Resultis a union withOkandErrdata variants.@AnyResultsemantic documentation.@OkTypeand@ErrTypeprovide type hints forResultpayloads.Err.errormust satisfy@Has(Error).panicto immediately terminate execution with an error message.
For the !!, !. operators, the VM needs to look at the presence of the @prelude.Error attribute. This should be doable with a deref of the underlying type and checking for the attribute presence using a Type ID.
If present, the value is treated as an error. Otherwise it is treated as a normal value.
If these values are not nested in Ok or Err, they will be wrapped accordingly.
The goal is to support strongly typed result types like PersonResult.
attr Error {
@Bound()
@Returns(String)
debug(err)
}
attr AnyResult {}
@AnyResult()
union Result {
data Ok {
value
}
@Error({ err -> err.error.debug(err.error) })
data Err {
@Has(Error)
error
}
}
attr OkType {
@Type(AnyType) type
}
attr ErrType {
@Type(AnyType) type
}
Changes to the Standard Library
- Add
@Error,@AnyResult,Result,Ok,Err,@OkType, and@ErrTypein
prelude. panic(@String str)to immediately terminate execution with an error message.
Alternatives Considered
- Exceptions or panics, which do not fit the explicit, attribute-driven model.
- Returning
Option, which loses error context and diagnostics.
Acknowledgements
- Influenced by Rust’s
Result<T, E>and Swift’sResulttype.