{"id":29698495,"url":"https://github.com/loopperfect/neither","last_synced_at":"2025-07-23T10:08:00.093Z","repository":{"id":91181005,"uuid":"90875065","full_name":"LoopPerfect/neither","owner":"LoopPerfect","description":"Either and Maybe monads for better error-handling in C++ ↔️","archived":false,"fork":false,"pushed_at":"2019-10-31T09:00:26.000Z","size":1129,"stargazers_count":248,"open_issues_count":4,"forks_count":18,"subscribers_count":17,"default_branch":"master","last_synced_at":"2023-10-25T23:01:39.405Z","etag":null,"topics":["buckaroo","cpp","either","functional-programming","header-only","maybe","monads","optional"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LoopPerfect.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-05-10T14:42:25.000Z","updated_at":"2023-09-30T19:07:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"9529b0c7-c4c2-4b7a-81e3-6076d278b5b7","html_url":"https://github.com/LoopPerfect/neither","commit_stats":null,"previous_names":[],"tags_count":5,"template":null,"template_full_name":null,"purl":"pkg:github/LoopPerfect/neither","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoopPerfect%2Fneither","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoopPerfect%2Fneither/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoopPerfect%2Fneither/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoopPerfect%2Fneither/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LoopPerfect","download_url":"https://codeload.github.com/LoopPerfect/neither/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LoopPerfect%2Fneither/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266658206,"owners_count":23963652,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["buckaroo","cpp","either","functional-programming","header-only","maybe","monads","optional"],"created_at":"2025-07-23T10:07:58.235Z","updated_at":"2025-07-23T10:08:00.075Z","avatar_url":"https://github.com/LoopPerfect.png","language":"C++","readme":"# neither\n\nA functional implementation of Either in C++14.\n\n[![Travis](https://img.shields.io/travis/LoopPerfect/neither.svg)](https://travis-ci.org/LoopPerfect/neither) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/LoopPerfect/neither/master/license)\n\n```\nbuckaroo add github.com/loopperfect/neither\n```\n\n## Examples\n\n\n### Handling Unsafe Code\n```c++\n\nauto unsafe = [] { // a function that throws, sometimes we can't avoid it...\n  if (true) {\n    throw std::runtime_error(\"error\");\n  }\n  return 1;\n}\n\nEither\u003cstd::exception, int\u003e e = Try\u003cstd::exception\u003e(unsafe); // let's lift the exception into the typesystem\n\ne.left()\n  .map([](auto const\u0026 e) {\n    return std::cerr \u003c\u003c e.what() \u003c\u003c std::endl;\n  }); // print error if available\n\nint result = e\n  .leftMap([](auto) { return 42; }) // do nothing with exception and map to 42\n  .rightMap([](auto x) { return x * 2; }) // do further computation if value available\n  .join() // join both sides of either\n\nASSERT_TRUE(result == 42);\n\n```\n\n### Another Example\n\n```c++\nEither\u003cstd::string, int\u003e compute(int x) {\n  if(x\u003c0) return left(\"don't pass x\u003c0\");\n  return right(x*x);\n}\n\nstd::string resultString = compute(5)\n  .rightMap([](auto x){ return x/2.0;}) // success case\n  .join(\n    [](auto errorStr) { return \"compute said: \" + errorStr; }, // error-case\n    [](auto x) { return \"compute said: \" + std::to_string(x); } // success-case\n   );\n\nstd::cout \u003c\u003c resultString \u003c\u003c std::endl;\n\n```\n\n### Composition of Eithers\n\n```c++\n\nneither::Either\u003cmy_error_t, int\u003e f1();\nneither::Either\u003cmy_error_t, float\u003e f2();\n\nvoid compose() {\n    auto value = f1()\n        .rightFlatMap([](const struct_a\u0026 v){\n            return f2();\n        })\n        .rightMap([](const struct_b\u0026 v){\n            return 5;\n        })\n        .leftMap([](const auto\u0026 my_error){\n            return 6;\n        }).join();\n    // value should be either 5 or 6\n}\n\n```\n\n### Maybe Example\n\n```c++\n\nMaybe\u003cfloat\u003e compute(float x) {\n  if(x\u003c0) return {};\n  return {sqrtf(x)};\n}\n\nMaybe\u003cfloat\u003e x = compute(-4)\n .map([](auto x){ return x*x;})\n .map([](auto x){ return x+1 });\n\nif(!x.hasValue) {\n  std::cerr \u003c\u003c \"error occured\" \u003c\u003c std::endl;\n}\n\n```\n\n### Monadic Lifting\n\n```c++\n\nint sum(int x, int y){ return x+y; }\n\n//...\n\nauto monadicSum = lift(sum); // transforms sum to: Maybe\u003cint\u003e MonadicSum(Maybe\u003cint\u003e, Maybe\u003cint\u003e)\n\nASSERT_TRUE( monadicSum( maybe(5) , maybe(7) ).get(0) == 12 );\nASSERT_TRUE( monadicSum( maybe(), maybe(1) ).hasValue == false);\n\n```\n\n## Why Eithers? - Learned Lessons About Error handling\n\nSome useful references:\n\n - Benchmarks: [Mongrel Monads, Dirty, Dirty, Dirty - Niall Douglas [ACCU 2017]](https://youtu.be/XVofgKH-uu4?t=1h)\n\n - Why out-parameters are bad from the perspective of optimizers:\n[2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++](https://youtu.be/eR34r7HOU14?t=38m)\n\n### Summary and Conclusions\n\n- Error codes break composition\n  - requires out-parameters; making functions impure and hard to reason about\n  - using out-parameters makes inlining harder\n  - =\u003e don't use output parameters\n- Exceptions are 2-3 orders of magnitude slower if exceptions are thrown\n  - =\u003e avoid throwing exceptions - not always possible\n- Overhead of exceptions grows linear with the callstack\n  - =\u003e catch exceptions early\n- Exceptions are not part of the type-system\n  - annotating function signatures with `throw` and `noexcept` is not helpful;\n    contract breaches are not detected in compile-time but call `std::terminate` in run-time\n  - handling exceptions is error prone and requires documentation\n  - =\u003e encode errors in the types to enforce proper handling by the API consumer\n\n## Installation\n\nThis library requires a C++ 14 compiler.\n\nInstall with [Buckaroo](https://buckaroo.pm):\n\n```\nbuckaroo add github.com/loopperfect/neither\n```\n\nThe [Buck](https://www.buckbuild.com) target is `:neither`\n\nAlternatively you can copy \u0026 paste the headers to your include path:\n\n```\ncp neither/include/*.hpp $InstallPath/include/neither\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floopperfect%2Fneither","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Floopperfect%2Fneither","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floopperfect%2Fneither/lists"}