// // either_stream.hpp // optional.stream // // Created by Sam Jaffe on 1/28/17. // #pragma once #include #include template using either = std::variant; namespace stream { template class either_stream; } namespace stream::traits { template struct either_traits { static constexpr bool const is_either = false; }; template struct either_traits<::either> { static constexpr bool const is_either = true; using stream_type = either_stream; using value_type = T; using error_type = E; }; template using either_stream_t = typename either_traits::stream_type; template using either_value_t = typename either_traits::value_type; template inline constexpr bool is_either_v = either_traits::is_either; } namespace stream { template class either_stream { private: template using result_of = decltype(std::declval()(std::declval())); private: ::either value_; public: either_stream(E const & v) : value_(v) {} either_stream(E && v) : value_(std::forward(v)) {} either_stream(T const & v) : value_(v) {} either_stream(T && v) : value_(std::forward(v)) {} either_stream(::either const & v) : value_(v) {} either_stream(::either && v) : value_(std::forward<::either>(v)) {} template auto map(F && fun) const -> either_stream, E> { static_assert(!traits::is_either_v>, "Cannot nest eithers"); if (value_.index() == 0) { return fun(std::get<0>(value_)); } else { return std::get<1>(value_); } } template auto flatmap(F && fun) const -> either_stream>, E> { if (value_.index() == 0) { return fun(std::get<0>(value_)); } else { return std::get<1>(value_); } } template auto match(FT && left, FE && right) -> std::common_type_t, result_of> { if (value_.index() == 0) { return left(std::get<0>(value_)); } else { return right(std::get<1>(value_)); } } }; } namespace stream::either { /** * Construct a stream from the given type while explicitly providing both the * type and the error class */ template either_stream make_stream(T const & opt) { return {{opt}}; } /** * Construct a stream from the given type where the real type is an inferred * property */ template either_stream make_stream(T const & opt) { return {{opt}}; } /** * Construct a stream with the given either-type as a template parameter. */ template auto make_stream(traits::either_value_t const & opt) { return traits::either_stream_t{opt}; } }