Last active
July 9, 2020 15:27
-
-
Save 3noch/6024523 to your computer and use it in GitHub Desktop.
An implementation of Haskell's "Either" type C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <boost/optional.hpp> | |
/** | |
* Wraps any value with a context of Left for the Either class. | |
* | |
* By convention, Left represents some sort of less-desired condition. | |
*/ | |
template<typename T> | |
class Left | |
{ | |
public: | |
Left(T v) : v_(v) {} | |
const T & value() const throw () { return v_; } | |
private: | |
T v_; | |
}; | |
/** | |
* Equality comparisons for Left. | |
*/ | |
template<typename L> bool operator==(const Left<L> & x, const Left<L> & y) { return x.value() == y.value(); } | |
template<typename L> bool operator!=(const Left<L> & x, const Left<L> & y) { return !(x == y); } | |
template<typename L> bool operator==(const Left<L> & x, const L & y) { return x.value() == y; } | |
template<typename L> bool operator!=(const Left<L> & x, const L & y) { return !(x == y); } | |
template<typename L> bool operator==(const L & x, const Left<L> & y) { return x == y.value(); } | |
template<typename L> bool operator!=(const L & x, const Left<L> & y) { return !(x == y); } | |
/** | |
* Wraps any value with a context of Right for the Either class. | |
* | |
* By convention, Right represents some sort of good condition. | |
*/ | |
template<typename T> | |
class Right | |
{ | |
public: | |
Right(T v) : v_(v) {} | |
const T & value() const throw () { return v_; } | |
private: | |
T v_; | |
}; | |
/** | |
* Equality comparisons for Right. | |
*/ | |
template<typename R> bool operator==(const Right<R> & x, const Right<R> & y) { return x.value() == y.value(); } | |
template<typename R> bool operator!=(const Right<R> & x, const Right<R> & y) { return !(x == y); } | |
template<typename R> bool operator==(const Right<R> & x, const R & y) { return x.value() == y; } | |
template<typename R> bool operator!=(const Right<R> & x, const R & y) { return !(x == y); } | |
template<typename R> bool operator==(const R & x, const Right<R> & y) { return x == y.value(); } | |
template<typename R> bool operator!=(const R & x, const Right<R> & y) { return !(x == y); } | |
/** | |
* Like Haskell's Either data type, wraps a value as either a Left or Right. | |
*/ | |
template<typename L, typename R> | |
class Either | |
{ | |
public: | |
typedef L LeftType; | |
typedef R RightType; | |
Either(R right) : right_(right) {} | |
Either(Left<L> left) : left_(left.value()) {} | |
Either(Right<R> right) : right_(right.value()) {} | |
Either(bool useRight, L left, R right) | |
{ | |
if (useRight) { right_ = right; } | |
else { left_ = left; } | |
} | |
bool isLeft() const throw () { return left_; } | |
bool isRight() const throw () { return right_; } | |
const R & operator*() const { return *right_; } | |
const R & right() const { return *right_; } | |
const L & left() const { return *left_; } | |
const R * operator->() const { return &*right_; } | |
operator bool() const throw () { return isRight(); } | |
private: | |
boost::optional<L> left_; | |
boost::optional<R> right_; | |
}; | |
/** | |
* Equality comparisons for Either. | |
*/ | |
template<typename L, typename R> bool operator==(const Either<L, R> & x, const Either<L, R> & y) | |
{ | |
if ( x && y) { return *x == *y; } | |
if (!x && !y) { return x.left() == y.left(); } | |
return false; | |
} | |
template<typename L, typename R> bool operator!=(const Either<L, R> & x, const Either<L, R> & y) { return !(x == y); } | |
template<typename L, typename R> bool operator==(const Either<L, R> & x, const R & y) { return x == Either<L, R>(y); } | |
template<typename L, typename R> bool operator!=(const Either<L, R> & x, const R & y) { return !(x == y); } | |
template<typename L, typename R> bool operator==(const R & x, const Either<L, R> & y) { return Either<L, R>(x) == y; } | |
template<typename L, typename R> bool operator!=(const R & x, const Either<L, R> & y) { return !(x == y); } | |
template<typename L, typename R> bool operator==(const Right<R> & x, const Either<L, R> & y) { return Either<L, R>(x) == y; } | |
template<typename L, typename R> bool operator!=(const Right<R> & x, const Either<L, R> & y) { return !(x == y); } | |
template<typename L, typename R> bool operator==(const Either<L, R> & x, const Right<R> & y) { return x == Either<L, R>(y); } | |
template<typename L, typename R> bool operator!=(const Either<L, R> & x, const Right<R> & y) { return !(x == y); } | |
template<typename L, typename R> bool operator==(const Left<L> & x, const Either<L, R> & y) { return Either<L, R>(x) == y; } | |
template<typename L, typename R> bool operator!=(const Left<L> & x, const Either<L, R> & y) { return !(x == y); } | |
template<typename L, typename R> bool operator==(const Either<L, R> & x, const Left<L> & y) { return x == Either<L, R>(y); } | |
template<typename L, typename R> bool operator!=(const Either<L, R> & x, const Left<L> & y) { return !(x == y); } | |
template<typename T> bool operator==(const Right<T> & x, const Left<T> & y) { return false; } | |
template<typename T> bool operator!=(const Right<T> & x, const Left<T> & y) { return !(x == y); } | |
template<typename T> bool operator==(const Left<T> & x, const Right<T> & y) { return false; } | |
template<typename T> bool operator!=(const Left<T> & x, const Right<T> & y) { return !(x == y); } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <boost/test/unit_test.hpp> | |
#include <Either.h> | |
static Either<int, int> testImplicitLeft() | |
{ | |
return Left<int>(0); | |
} | |
BOOST_AUTO_TEST_CASE(testEitherLeft) | |
{ | |
Either<int, int> v = Left<int>(0); | |
BOOST_CHECK(v.isLeft()); | |
BOOST_CHECK(!v); | |
BOOST_CHECK(!v.isRight()); | |
BOOST_CHECK_EQUAL(v.left(), 0); | |
BOOST_CHECK(!testImplicitLeft()); | |
} | |
static Either<int, int> testImplicitRight() | |
{ | |
return 0; | |
} | |
BOOST_AUTO_TEST_CASE(testEitherRight) | |
{ | |
Either<int, int> v(Right<int>(0)); | |
BOOST_CHECK(!v.isLeft()); | |
BOOST_CHECK(v); | |
BOOST_CHECK(v.isRight()); | |
BOOST_CHECK_EQUAL(v.right(), 0); | |
BOOST_CHECK_EQUAL(*v, 0); | |
BOOST_CHECK(testImplicitRight()); | |
} | |
BOOST_AUTO_TEST_CASE(testEitherAssignment) | |
{ | |
Either<int, bool> v(true); | |
BOOST_CHECK(v); | |
BOOST_CHECK(*v); | |
v = Right<bool>(false); | |
BOOST_CHECK(v); | |
BOOST_CHECK(!*v); | |
v = Left<int>(0); | |
BOOST_CHECK(!v); | |
BOOST_CHECK_EQUAL(v.left(), 0); | |
} | |
BOOST_AUTO_TEST_CASE(testEitherEquality) | |
{ | |
const Either<int, bool> rightTrue(true); | |
const Either<int, bool> rightFalse(false); | |
const Either<int, bool> left0(Left<int>(0)); | |
const Either<int, bool> left1(Left<int>(1)); | |
BOOST_CHECK(rightTrue == rightTrue); | |
BOOST_CHECK(rightTrue == true); | |
BOOST_CHECK(true == rightTrue); | |
BOOST_CHECK(rightTrue != rightFalse); | |
BOOST_CHECK(rightFalse != rightTrue); | |
BOOST_CHECK(rightTrue != false); | |
BOOST_CHECK(false != rightTrue); | |
BOOST_CHECK(rightTrue != Left<int>(0)); | |
BOOST_CHECK(Left<int>(0) != rightTrue); | |
BOOST_CHECK(left0 == left0); | |
BOOST_CHECK(left0 == Left<int>(0)); | |
BOOST_CHECK(Left<int>(0) == left0); | |
BOOST_CHECK(left0 != left1); | |
BOOST_CHECK(left1 != left0); | |
BOOST_CHECK(Left<int>(0) == Left<int>(0)); | |
BOOST_CHECK(0 == Left<int>(0)); | |
BOOST_CHECK(Left<int>(0) == 0); | |
BOOST_CHECK(Left<int>(0) != Left<int>(1)); | |
BOOST_CHECK(Left<int>(1) != Left<int>(0)); | |
BOOST_CHECK(1 != Left<int>(0)); | |
BOOST_CHECK(Left<int>(0) != 1); | |
BOOST_CHECK(Right<int>(0) == Right<int>(0)); | |
BOOST_CHECK(0 == Right<int>(0)); | |
BOOST_CHECK(Right<int>(0) == 0); | |
BOOST_CHECK(Right<int>(0) != Right<int>(1)); | |
BOOST_CHECK(Right<int>(1) != Right<int>(0)); | |
BOOST_CHECK(1 != Right<int>(0)); | |
BOOST_CHECK(Right<int>(0) != 1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment