hd-lang

#Result

Status: standard library specification draft.

This chapter defines the part of std.result that lib/std writes in ordinary hd over the language tier. That is the inherent methods and_then, map_err, unwrap_or, map, ok, err, is_ok, is_err, and expect of Result[T, E].

The language tier keeps what the compiler knows by name (Result Types):

Item Why it stays in the language tier
Result[T, E], .Ok, and .Err the prelude enum and its construction rules
postfix ? on a Result Result Propagation, which may convert the error through From
invariance in T and E types.option.invariant.result
a variant constructor as a function value data.enum.fn-value.use, as in map_err(ConfigError.Number)

Note. std.error declares one more inherent method of Result[T, E], for E < Error: context, which wraps an error with a message. See Error Context.

#Result Methods

std.result gives every Result[T, E] these methods:

use std.num.{ParseNumberError}

enum ConfigError:
    Missing
    Number(error: ParseNumberError)

fn setting(settings: Map[string, string], key: string) -> Result[string, ConfigError]:
    match settings.get(key):
        .Some(text) => .Ok(text)
        .None => .Err(ConfigError.Missing)

fn number(text: string) -> Result[i32, ConfigError]:
    text.parse::[i32]().map_err(ConfigError.Number)

fn port(settings: Map[string, string]) -> i32:
    setting(settings, "port").and_then(number).unwrap_or(8080)
Rule Method Result
std-result.and-thenand_then fn and_then[U](self, transform: fn(T) -> Result[U, E]) -> Result[U, E] transform(value) for .Ok(value), and .Err(error) for .Err(error)
std-result.map-errmap_err fn map_err[F](self, transform: fn(E) -> F) -> Result[T, F] .Ok(value) for .Ok(value), and .Err(transform(error)) for .Err(error)
std-result.unwrap-orunwrap_or fn unwrap_or(self, fallback: T) -> T value for .Ok(value), and fallback for .Err(_)
  1. std-result.methods.inherentThe three are inherent methods that std declares on Result[T, E], so every result has them without a use, by trait.own.inherent.std.no-use.
  2. std-result.and-then.onceand_then calls transform once for .Ok, and never for .Err.
  3. std-result.map-err.oncemap_err calls transform once for .Err, and never for .Ok.
  4. std-result.and-then.same-errorand_then keeps the error type E, and converts no error. A transform whose result has another error type is an error. Error: type-mismatch.
  5. std-result.methods.non-suspendingNo method suspends. Each transform callback is a plain function type with the empty row.
Error example
use std.num.{ParseNumberError}

enum ConfigError:
    Missing
    Number(error: ParseNumberError)

fn setting(settings: Map[string, string], key: string) -> Result[string, ConfigError]:
    match settings.get(key):
        .Some(text) => .Ok(text)
        .None => .Err(ConfigError.Missing)

fn parsed(text: string) -> Result[i32, ParseNumberError]:
    text.parse::[i32]()

fn port(settings: Map[string, string]) -> Result[i32, ConfigError]:
    setting(settings, "port").and_then(parsed)  # error: type-mismatch

Note. A step with another error type is mapped first, as and_then(number) does above with map_err. Postfix ? is the one place an error converts through From by itself.

Note. fallback is an ordinary argument, so it is evaluated before the call, even when the result is .Ok.

Why. Postfix ? works only where the enclosing function or closure returns a Result. and_then chains two fallible steps in any expression, as Rust's Result::and_then does.

#Mapping The Value

map transforms the value of .Ok, as map on T? transforms the value of .Some:

use std.num.{ParseNumberError}

fn doubled(text: string) -> Result[i32, ParseNumberError]:
    text.parse::[i32]().map(fn(value: i32) -> i32: value * 2)
Rule Method Result
std-result.mapmap fn map[U](self, transform: fn(T) -> U) -> Result[U, E] .Ok(transform(value)) for .Ok(value), and .Err(error) for .Err(error)
  1. std-result.map.inherentmap is an inherent method that std declares on Result[T, E] as well, so it needs no use either.
  2. std-result.map.oncemap calls transform once for .Ok, and never for .Err.
  3. std-result.map.non-suspendingmap does not suspend. Its transform callback is a plain function type with the empty row.

Why. One name, map, transforms the success value of every container: a list, an optional, and a result. map_err names the one transform of the other side.

#Variant Tests And Conversions

std.result also gives every Result[T, E] five methods that test the variant, or turn it into a value:


fn port(text: string) -> i32?:
    text.parse::[i32]().ok()

fn invalid(text: string) -> bool:
    text.parse::[i32]().is_err()

fn configured_port(text: string) -> i32:
    text.parse::[i32]().expect("the configured port is a number")
Rule Method Result
std-result.okok fn ok(self) -> T? .Some(value) for .Ok(value), and .None for .Err(_)
std-result.errerr fn err(self) -> E? .None for .Ok(_), and .Some(error) for .Err(error)
std-result.is-okis_ok fn is_ok(self) -> bool true for .Ok(_), and false for .Err(_)
std-result.is-erris_err fn is_err(self) -> bool false for .Ok(_), and true for .Err(_)
std-result.expectexpect fn expect(self, message: string) -> T for E < Display value for .Ok(value), and a panic for .Err(error)
  1. std-result.tests.inherentThe five are inherent methods that std declares on Result[T, E] as well, so they need no use either.
  2. std-result.expect.boundexpect is available when E < Display, as std declares it in an impl with that bound. Every Error is Display, so every Result[T, E] with E < Error has it.
  3. std-result.expect.panicexpect on .Err(error) panics with the text message: error: message, a colon and a space, then the Display text of error. Its category is explicit-panic. Panic: explicit-panic.
  4. std-result.tests.non-suspendingNone of the five suspends.

Note. message is an ordinary argument, so it is evaluated before the call, even when the result is .Ok. The text of error is built on the panic path only.

Why. expect shows the error beside the message, as Rust's Result::expect does, so a panic report names both what the caller assumed and what failed.

See also: Option, Error Context, List And Optional Map, Runtime Panics, Variance.