Skip to content

Result values and operations

Result<TValue, TError> represents expected failure as an ordinary value. It is a built-in source type backed by the E#-authored CLR value type Esharp.Stdlib.Result`2; E# and C# therefore exchange the same closed generic struct without an adapter.

ResultType = "Result" "<" Type "," Type ">" .
ResultConstructor = "ok" "(" Expr ")" | "error" "(" Expr ")" .
ResultCaseView = ".ok" "(" identifier ")" | ".err" "(" identifier ")" .

The value has one discriminant and two payload slots:

pub struct Result<TValue, TError> {
pub IsOk: bool
pub Value: TValue
pub Error: TError
}

It is a value struct: assignment copies it; it has no identity; and construction does not allocate. The variant invariant is IsOk iff Value is meaningful, and !IsOk iff Error is meaningful. The compiler’s ok / error intrinsics initialize the discriminant and the selected payload directly.

union ParseError { malformed, outOfRange }
func parse(text: string) -> Result<int, ParseError> {
if text.Length == 0 { return error(.malformed) }
return ok(int.Parse(text))
}

The expression’s contextual Result<T, E> type determines whether a dot case in error(.malformed) is a valid error value. A program may also write the explicit static factory:

let success = Result.Ok<int, string>(42)
let failure = Result.Error<int, string>("bad input")

Result<T, E> and the arity-zero static Result factory coexist by arity keying. The factory is an ordinary CLR static surface for explicit type arguments and C# interop; ok and error are the idiomatic E# construction forms.

AccessorResult
.IsOkstored discriminant
.IsErrorsynthesized negation of IsOk
.Valuesuccess payload; throws InvalidOperationException on error
.Errorerror payload; throws InvalidOperationException on success

A match over .ok(value) and .err(error) is exhaustive and projects the appropriate payload:

func label(r: Result<int, string>) -> string = match r {
.ok(value) => "value {value}"
.err(error) => "error {error}"
}

The ten Result combinators are value-receiver methods. They never mutate the input Result; each returns a transformed or observed value.

MethodResult
Map(f) / MapErr(f)transforms only success / error payload
Bind(f)chains a success into another Result
Match(ok, err)folds both cases to one output type
Inspect(f) / InspectErr(f)observes one case and returns the original result
UnwrapOr(x) / UnwrapOrElse(f)returns a success value or fallback
Unwrap() / UnwrapErr()returns a payload or throws on the opposite case

Generic inference flows through a receiver and lambda body, so r.Map((x) => x + 1) closes the new success type from the lambda result. Unwrap and direct .Value are deliberate exceptional escape hatches; normal expected-failure flow uses match, combinators, or postfix ?.