|
|
@@ -8,83 +8,102 @@
|
|
|
#pragma once
|
|
|
|
|
|
#include <type_traits>
|
|
|
-
|
|
|
-#if __cplusplus > 201402L
|
|
|
#include <variant>
|
|
|
-template <typename T, typename E>
|
|
|
-using either = std::variant<T, E>;
|
|
|
-#else
|
|
|
-#include "variant/variant.hpp"
|
|
|
-template <typename T, typename E>
|
|
|
-using either = variant<T, E>;
|
|
|
-#endif
|
|
|
+
|
|
|
+template <typename T, typename E> using either = std::variant<T, E>;
|
|
|
+
|
|
|
+namespace stream {
|
|
|
+ template <typename T> class either_stream;
|
|
|
+
|
|
|
+ template <typename T> struct either_left;
|
|
|
+ template <typename T, typename E> struct either_left<::either<T, E>> {
|
|
|
+ using type = T;
|
|
|
+ };
|
|
|
+
|
|
|
+ template <typename T> using either_left_t = typename either_left<T>::type;
|
|
|
+
|
|
|
+ template <typename T, typename E> class either_stream<::either<T, E>> {
|
|
|
+ private:
|
|
|
+ template <typename F, typename T2 = T>
|
|
|
+ using map_f = decltype(std::declval<F>()(std::declval<T2>()));
|
|
|
+
|
|
|
+ template <typename F, typename T2 = T>
|
|
|
+ using flatmap_f = either_left_t<map_f<F, T2>>;
|
|
|
+
|
|
|
+ private:
|
|
|
+ ::either<T, E> value_;
|
|
|
+
|
|
|
+ public:
|
|
|
+ either_stream(E const & v) : value_(v) {}
|
|
|
+ either_stream(E && v) : value_(std::forward<E>(v)) {}
|
|
|
+
|
|
|
+ either_stream(T const & v) : value_(v) {}
|
|
|
+ either_stream(T && v) : value_(std::forward<T>(v)) {}
|
|
|
+ either_stream(::either<T, E> const & v) : value_(v) {}
|
|
|
+ either_stream(::either<T, E> && v)
|
|
|
+ : value_(std::forward<::either<T, E>>(v)) {}
|
|
|
+
|
|
|
+ template <typename F>
|
|
|
+ either_stream<::either<map_f<F>, E>> map(F && fun) const {
|
|
|
+ using next_t = either_stream<::either<map_f<F>, E>>;
|
|
|
+ return value_.index() == 0 ? next_t{fun(std::get<0>(value_))}
|
|
|
+ : next_t{std::get<1>(value_)};
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename F>
|
|
|
+ either_stream<::either<flatmap_f<F>, E>> flatmap(F && fun) const {
|
|
|
+ using next_t = either_stream<::either<flatmap_f<F>, E>>;
|
|
|
+ return value_.index() == 0 ? next_t{fun(std::get<0>(value_))}
|
|
|
+ : next_t{std::get<1>(value_)};
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename FT, typename FE,
|
|
|
+ typename = typename std::enable_if<
|
|
|
+ !(std::is_void<map_f<FT>>::value &&
|
|
|
+ std::is_void<map_f<FE, E>>::value)>::type>
|
|
|
+ typename std::common_type<map_f<FT>, map_f<FE, E>>::type
|
|
|
+ match(FT && left, FE && right) {
|
|
|
+ return value_.index() == 0 ? left(std::get<0>(value_))
|
|
|
+ : right(std::get<1>(value_));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename FT, typename FE,
|
|
|
+ typename = typename std::enable_if<
|
|
|
+ std::is_void<map_f<FT>>::value &&
|
|
|
+ std::is_void<map_f<FE, E>>::value>::type>
|
|
|
+ void match(FT && left, FE && right) {
|
|
|
+ value_.index() == 0 ? left(std::get<0>(value_))
|
|
|
+ : right(std::get<1>(value_));
|
|
|
+ }
|
|
|
+
|
|
|
+ ::either<T, E> const & value() const { return value_; }
|
|
|
+ };
|
|
|
+}
|
|
|
|
|
|
namespace stream { namespace either {
|
|
|
- namespace detail {
|
|
|
- template <typename T> class either_stream;
|
|
|
-
|
|
|
- template <typename T> struct either_left;
|
|
|
- template <typename T, typename E>
|
|
|
- struct either_left<::either<T, E>> { using type = T; };
|
|
|
-
|
|
|
- template <typename T, typename E>
|
|
|
- class either_stream<::either<T, E>> {
|
|
|
- private:
|
|
|
- template <typename F, typename T2>
|
|
|
- using map_f = decltype(std::declval<F>()(std::declval<T2>()));
|
|
|
-
|
|
|
- template <typename F, typename T2>
|
|
|
- using flatmap_f = typename either_left<decltype(std::declval<F>()(std::declval<T2>()))>::type;
|
|
|
-
|
|
|
- public:
|
|
|
- either_stream(E const & v) : value_(v) {}
|
|
|
- either_stream(E && v) : value_(std::forward<E>(v)) {}
|
|
|
-
|
|
|
- either_stream(T const & v) : value_(v) {}
|
|
|
- either_stream(T && v) : value_(std::forward<T>(v)) {}
|
|
|
- either_stream(::either<T, E> const & v) : value_(v) {}
|
|
|
- either_stream(::either<T, E> && v) : value_(std::forward<::either<T, E>>(v)) {}
|
|
|
-
|
|
|
- template <typename F>
|
|
|
- either_stream<::either<map_f<F, T>, E>> map(F && fun) const {
|
|
|
- using next_t = either_stream<::either<map_f<F, T>, E>>;
|
|
|
- return value_.index() == 0 ? next_t{fun(std::get<0>(value_))} : next_t{std::get<1>(value_)};
|
|
|
- }
|
|
|
-
|
|
|
- template <typename F>
|
|
|
- either_stream<::either<flatmap_f<F, T>, E>> flatmap(F && fun) const {
|
|
|
- using next_t = either_stream<::either<flatmap_f<F, T>, E>>;
|
|
|
- return value_.index() == 0 ? next_t{fun(std::get<0>(value_))} : next_t{std::get<1>(value_)};
|
|
|
- }
|
|
|
-
|
|
|
- template <typename FT, typename FE, typename = typename std::enable_if<!(std::is_void<map_f<FT, T>>::value && std::is_void<map_f<FE, E>>::value)>::type>
|
|
|
- typename std::common_type<map_f<FT, T>, map_f<FE, E>>::type match(FT && left, FE && right) {
|
|
|
- return value_.index() == 0 ? left(std::get<0>(value_)) : right(std::get<1>(value_));
|
|
|
- }
|
|
|
-
|
|
|
- template <typename FT, typename FE, typename = typename std::enable_if<std::is_void<map_f<FT, T>>::value && std::is_void<map_f<FE, E>>::value>::type>
|
|
|
- void match(FT && left, FE && right) {
|
|
|
- value_.index() == 0 ? left(std::get<0>(value_)) : right(std::get<1>(value_));
|
|
|
- }
|
|
|
-
|
|
|
- ::either<T, E> const & value() const { return value_; }
|
|
|
- private:
|
|
|
- ::either<T, E> value_;
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
+ /**
|
|
|
+ * Construct a stream from the given type while explicitly providing both the
|
|
|
+ * type and the error class
|
|
|
+ */
|
|
|
template <typename T, typename E>
|
|
|
- auto make_stream(T const & opt) -> detail::either_stream<::either<T, E>> {
|
|
|
+ auto make_stream(T const & opt) -> either_stream<::either<T, E>> {
|
|
|
return {{opt}};
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Construct a stream from the given type where the real type is an inferred
|
|
|
+ * property
|
|
|
+ */
|
|
|
template <typename E, typename T>
|
|
|
- auto make_stream(T const & opt) -> detail::either_stream<::either<T, E>> {
|
|
|
+ auto make_stream(T const & opt) -> either_stream<::either<T, E>> {
|
|
|
return {{opt}};
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Construct a stream with the given either-type as a template parameter.
|
|
|
+ */
|
|
|
template <typename E>
|
|
|
- auto make_stream(typename detail::either_left<E>::type const & opt) -> detail::either_stream<E> {
|
|
|
+ auto make_stream(either_left_t<E> const & opt) -> either_stream<E> {
|
|
|
return {{opt}};
|
|
|
}
|
|
|
-} }
|
|
|
+}}
|