Program Listing for File monad.hpp
↰ Return to documentation for file (include/rsl/monad.hpp)
#pragma once
#include <tl_expected/expected.hpp>
#include <optional>
namespace rsl {
template <typename T, typename Fn>
[[nodiscard]] constexpr auto mbind(std::optional<T> const& opt,
Fn fn) -> std::invoke_result_t<Fn, T> {
static_assert(std::is_convertible_v<std::nullopt_t, std::invoke_result_t<Fn, T>>,
"Fn must return a std::optional");
if (opt) return fn(opt.value());
return std::invoke_result_t<Fn, T>{std::nullopt};
}
template <typename T, typename E, typename Fn>
[[nodiscard]] constexpr auto mbind(tl::expected<T, E> const& exp,
Fn fn) -> std::invoke_result_t<Fn, T> {
if (exp) return fn(exp.value());
return tl::unexpected(exp.error());
}
template <typename Fn>
[[nodiscard]] auto mtry(Fn fn) -> tl::expected<std::invoke_result_t<Fn>, std::exception_ptr> try {
return fn();
} catch (...) {
return tl::unexpected(std::current_exception());
}
template <typename Fn, typename G>
[[nodiscard]] constexpr auto mcompose(Fn fn, G g) {
return [=](auto value) { return mbind(fn(value), g); };
}
template <typename T, typename G, typename... Ts>
[[nodiscard]] constexpr auto mcompose(T t, G g, Ts... vars) {
auto exp = mcompose(t, g);
return mcompose(exp, vars...);
}
template <typename T, typename E>
[[nodiscard]] constexpr auto has_error(tl::expected<T, E> const& exp) {
return !exp.has_value();
}
template <typename T, typename E>
[[nodiscard]] constexpr auto has_value(tl::expected<T, E> const& exp) {
return exp.has_value();
}
template <typename E, typename... Args>
[[nodiscard]] constexpr auto maybe_error(tl::expected<Args, E>... args) {
auto maybe = std::optional<E>();
(
[&](auto& exp) {
if (maybe.has_value()) return;
if (has_error(exp)) maybe = exp.error();
}(args),
...);
return maybe;
}
template <typename>
constexpr inline bool is_optional_impl = false;
template <typename T>
constexpr inline bool is_optional_impl<std::optional<T>> = true;
template <typename T>
constexpr inline bool is_optional = is_optional_impl<std::remove_cv_t<std::remove_reference_t<T>>>;
} // namespace rsl
template <typename T, typename Fn, typename = std::enable_if_t<rsl::is_optional<T>>,
typename = std::enable_if_t<std::is_invocable_v<
Fn, typename std::remove_cv_t<std::remove_reference_t<T>>::value_type>>>
[[nodiscard]] constexpr auto operator|(T&& opt, Fn&& fn) {
return rsl::mbind(std::forward<T>(opt), std::forward<Fn>(fn));
}
template <typename T, typename E, typename Fn>
[[nodiscard]] constexpr auto operator|(tl::expected<T, E> const& exp, Fn fn) {
return rsl::mbind(exp, fn);
}
template <typename T, typename Fn, typename = std::enable_if_t<!rsl::is_optional<T>>>
[[nodiscard]] constexpr auto operator|(T&& val, Fn&& fn) ->
typename std::enable_if_t<std::is_invocable_v<Fn, T>, std::invoke_result_t<Fn, T>> {
return std::invoke(std::forward<Fn>(fn), std::forward<T>(val));
}