The type hana::optional represents an
Optional value whose optional-ness is known at compile-time
and it models, among others, the concept of Monad. Since it is a compile-time optional, the question of what happens at run-time if I try to hana::flatten a non-empty hana::optional which doesn't wrap another hana::optional makes no sense, because because the error would be caught at compile-time.
But what about a run-time optional?
Here's my attempt on making std::optional (and only it, I'm not making a general concept of a maybe, which also boost::optional would satisfy) a hana::Monad, and I'd like to know if this is the correct way of doing it:
#include <assert.h>
#include <boost/hana/chain.hpp>
#include <optional>
#include <stdexcept>
#include <type_traits>
namespace hana = boost::hana;
template<typename T, typename = void>
struct has_value_type : std::false_type {};
template<typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
template<typename T>
bool constexpr has_value_type_t = has_value_type<T>::value;
namespace boost::hana {
template<typename T>
struct transform_impl<std::optional<T>> {
template <typename Opt, typename F>
static constexpr auto apply(Opt&& o, F&& f) {
return o.has_value() ? std::make_optional(f(o.value())) : std::nullopt;
}
};
template<typename T>
struct flatten_impl<std::optional<T>> {
template <typename X>
static constexpr auto apply(X&& x) {
if constexpr (has_value_type_t<typename X::value_type>)
return (x.has_value() && x.value().has_value()) ? x.value() : std::nullopt;
else {
if (!x.has_value())
return std::nullopt;
else
throw std::runtime_error("std::optional<non_stdoptional_type> can't be flattened!");
}
}
};
template<>
struct flatten_impl<std::nullopt_t> {
template <typename X>
static constexpr auto apply(X&&) {
return std::nullopt;
}
};
}
int main() {
assert(hana::flatten(std::optional<std::optional<int>>{3}) == std::optional<int>{3});
assert(hana::flatten(std::optional<int>{}) == std::optional<int>{});
assert(hana::flatten(std::nullopt) == std::optional<int>{});
//assert(hana::flatten(std::optional<int>{2}) == std::optional<int>{}); // throws
}