Skip to content

Instantly share code, notes, and snippets.

@3noch
Last active July 9, 2020 15:27
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save 3noch/6024523 to your computer and use it in GitHub Desktop.
Save 3noch/6024523 to your computer and use it in GitHub Desktop.
An implementation of Haskell's "Either" type C++
#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); }
#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