The error takes the next train
An error has arrived at a function carrying a small overnight bag. We can inspect it, return it, or insist it should never have been allowed on the train.
Let a recoverable error return
These are different decisions.
import pooka/int
sum(a: String, b: String) -> Result(Int, int.ParseError) {
let first = int.parse(a)?
let second = int.parse(b)?
Ok(first + second)
}
main() {
assert sum("20", "22") == Ok(42)
assert sum("20", "owl") == Error(int.ParseError("owl"))
let assert Ok(value) = sum("20", "22")
assert value == 42
}
Unfold the punctuation
Here is the propagation written out in full:
import pooka/int
sum(a: String, b: String) -> Result(Int, int.ParseError) {
match int.parse(a) {
Error(reason) -> Error(reason)
Ok(first) -> match int.parse(b) {
Error(reason) -> Error(reason)
Ok(second) -> Ok(first + second)
}
}
}
main() {
assert sum("20", "22") == Ok(42)
assert sum("20", "owl") == Error(int.ParseError("owl"))
}
The question mark opens a Result. With Ok, execution continues using its contents. With Error, the surrounding function returns that error. The second parse does not run if the first one has already failed.
The function must itself return a compatible Result. Otherwise there would be nowhere honest to send the error.
An expectation is a stronger promise
let assert makes a stronger statement. It requires the pattern to match. If it does not, execution fails instead of returning the unmatched value as an ordinary error result.
For a fixed example string in a test, that expectation can be useful. For a stranger’s configuration file, it may be entirely the wrong hospitality. Invalid user input is usually a possibility the application should explain, rather than an impossible event it should crash over.
Neither spelling should hide repeated evaluation in generated code. If the expression reads a line or sends a request, evaluating it twice changes the program. Lowering the convenient syntax must preserve both the result and the number of times the work happens.
I like these small features when their meaning stays visible. ? passes a recoverable result back to the caller. let assert draws a line around an expectation the program will not continue without.
The punctuation is brief. The choice it records is not.
— Peregrine Finch