Skip to content

tchaloupka/expected

Repository files navigation

Expected

Latest version Dub downloads Actions Status codecov license

Implementation of the Expected idiom.

See the Andrei Alexandrescu’s talk (Systematic Error Handling in C++ and its slides.

Or more recent "Expect the Expected" by Andrei Alexandrescu for further background.

It is also inspired by C++'s proposed std::expected and Rust's Result.

Similar work is expectations by Paul Backus.

Features

  • lightweight, no other external dependencies
  • works with pure, @safe, @nogc, nothrow, and immutable
  • provides methods: ok, err, consume, expect, expectErr, andThen, orElse, map, mapError, mapOrElse
  • type inference for ease of use with ok and err
  • allows to use same types for T and E
  • allows to define Expected without value (void for T) - can be disabled with custom Hook
  • provides facility to change the Expected behavior by custom Hook implementation using the Design by introspection paradigm.
  • can enforce result check (with a cost)
  • can behave like a normal Exception handled code by changing the used Hook implementation
  • range interface

Documentation

View online on Github Pages

expected uses adrdox to generate it's documentation. To build your own copy, run the following command from the root of the expected repository:

path/to/adrdox/doc2 --genSearchIndex --genSource -o generated-docs source

Example usage

auto foo(int i) {
    if (i == 0) return err!int("oops");
    return ok(42 / i);
}

auto bar(int i) {
    if (i == 0) throw new Exception("err");
    return i-1;
}

// basic checks
assert(foo(2));
assert(foo(2).hasValue);
assert(!foo(2).hasError);
assert(foo(2).value == 21);

assert(!foo(0));
assert(!foo(0).hasValue);
assert(foo(0).hasError);
assert(foo(0).error == "oops");

// void result
assert(ok()); // no error -> success
assert(!ok().hasError);
// assert(err("foo").hasValue); // doesn't have hasValue and value properties

// expected from throwing function
assert(consume!bar(1) == 0);
assert(consume!bar(0).error.msg == "err");

// orElse
assert(foo(2).orElse!(() => 0) == 21);
assert(foo(0).orElse(100) == 100);

// andThen
assert(foo(2).andThen(foo(6)) == 7);
assert(foo(0).andThen(foo(6)).error == "oops");

// map
assert(foo(2).map!(a => a*2).map!(a => a - 2) == 40);
assert(foo(0).map!(a => a*2).map!(a => a - 2).error == "oops");

// mapError
assert(foo(0).mapError!(e => "OOPS").error == "OOPS");
assert(foo(2).mapError!(e => "OOPS") == 21);

// mapOrElse
assert(foo(2).mapOrElse!(v => v*2, e => 0) == 42);
assert(foo(0).mapOrElse!(v => v*2, e => 0) == 0);

See documentation for more usage examples.

Installation

If you're using dub, add the expected package to your project as a dependency.

Alternatively, since it's a single file self-contained implementation, you can simply copy expected.d to your project source directory and compile as usual.

Compilers compatibility

Build is tested against:

  • dmd-latest
  • dmd-2.095.1
  • dmd-2.094.2
  • dmd-2.091.1
  • ldc-latest
  • ldc-1.27.1
  • ldc-1.25.1
  • ldc-1.24.0