Skip to content
Either and Maybe monads for better error-handling in C++ ↔️
Branch: master
Clone or download
njlr Merge pull request #28 from ingve/master
Fix typo in README
Latest commit a7af11e Jan 18, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
neither Remove copy construction from argument to std::move in rightMap && Dec 10, 2018
travis * Upgrades to newer Buck Jan 9, 2019
.buckconfig
.clangformat clangformat May 11, 2017
.gitignore
.travis.yml
BUCK * Migrates to Buckaroo Redux Jan 9, 2019
LICENSE.txt * Tidying up Oct 12, 2017
README.md
buckaroo.lock.toml * Migrates to Buckaroo Redux Jan 9, 2019
buckaroo.toml * Migrates to Buckaroo Redux Jan 9, 2019
subdir_glob.bzl

README.md

neither

A functional implementation of Either in C++14.

Travis GitHub license

buckaroo add github.com/loopperfect/neither

Examples

Handling Unsafe Code

auto unsafe = [] { // a function that throws, sometimes we can't avoid it...
  if (true) {
    throw std::runtime_error("error");
  }
  return 1;
}

Either<std::exception, int> e = Try<std::exception>(unsafe); // let's lift the exception into the typesystem

e.left()
  .map([](auto const& e) {
    return std::cerr << e.what() << std::endl;
  }); // print error if available

int result = e
  .leftMap([](auto) { return 42; }) // do nothing with exception and map to 42
  .rightMap([](auto x) { return x * 2; }) // do further computation if value available
  .join() // join both sides of either

ASSERT_TRUE(result == 42);

Another Example

Either<std::string, int> compute(int x) {
  if(x<0) return left("don't pass x<0");
  return right(x*x);
}

std::string resultString = compute(5)
  .rightMap([](auto x){ return x/2.0;}) // success case
  .join(
    [](auto errorStr) { return "compute said: " + errorStr; }, // error-case
    [](auto x) { return "compute said: " + std::to_string(x); } // success-case
   );

std::cout << resultString << std::endl;

Composition of Eithers

neither::Either<my_error_t, int> f1();
neither::Either<my_error_t, float> f2();

void compose() {
    auto value = f1()
        .rightFlatMap([](const struct_a& v){
            return f2();
        })
        .rightMap([](const struct_b& v){
            return 5;
        })
        .leftMap([](const auto& my_error){
            return 6;
        }).join();
    // value should be either 5 or 6
}

Maybe Example

Maybe<float> compute(float x) {
  if(x<0) return {};
  return {sqrtf(x)};
}

Maybe<float> x = compute(-4)
 .map([](auto x){ return x*x;})
 .map([](auto x){ return x+1 });

if(!x.hasValue) {
  std::cerr << "error occured" << std::endl;
}

Monadic Lifting

int sum(int x, int y){ return x+y; }

//...

auto monadicSum = lift(sum); // transforms sum to: Maybe<int> MonadicSum(Maybe<int>, Maybe<int>)

ASSERT_TRUE( monadicSum( maybe(5) , maybe(7) ).get(0) == 12 );
ASSERT_TRUE( monadicSum( maybe(), maybe(1) ).hasValue == false);

Why Eithers? - Learned Lessons About Error handling

Some useful references:

Summary and Conclusions

  • Error codes break composition
    • requires out-parameters; making functions impure and hard to reason about
    • using out-parameters makes inlining harder
    • => don't use output parameters
  • Exceptions are 2-3 orders of magnitude slower if exceptions are thrown
    • => avoid throwing exceptions - not always possible
  • Overhead of exceptions grows linear with the callstack
    • => catch exceptions early
  • Exceptions are not part of the type-system
    • annotating function signatures with throw and noexcept is not helpful; contract breaches are not detected in compile-time but call std::terminate in run-time
    • handling exceptions is error prone and requires documentation
    • => encode errors in the types to enforce proper handling by the API consumer

Installation

This library requires a C++ 14 compiler.

Install with Buckaroo:

buckaroo add github.com/loopperfect/neither

The Buck target is :neither

Alternatively you can copy & paste the headers to your include path:

cp neither/include/*.hpp $InstallPath/include/neither
You can’t perform that action at this time.