微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 Hana 使 std::optional 成为运行时 monad?

如何解决如何使用 Hana 使 std::optional 成为运行时 monad?

类型 hana::optional 代表一个

在编译时已知其可选性的可选值

并且它模拟了 Monad 的概念等。由于它是编译时可选的,如果我尝试 hana::flatten 非空 hana::optional在运行时会发生什么问题包装另一个hana::optional是没有意义的,因为错误会在编译时被捕获。

但是运行时可选呢?

这是我尝试制作 std::optional(并且只是它,我没有制作可能的一般概念,它也 boost::optional 会满足)hana::Monad,并且我'想知道这是否是正确的做法:

#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
}

解决方法

可以进行一些改进。

Boost.Hana 使用标签调度系统来专门化 impl 结构。这简化了特化,但也防止了外部 impl 结构与内部函数模板的实例化的笛卡尔积。

此外,鉴于满足此约束的广泛类型集,您使用 value_type 检测嵌套的可选项也会出错。如果您使用 hana::is_a<optional_tag,X>,标签系统也会对此有所帮助。

这个更具体的约束还消除了您对嵌套可选项执行运行时检查的需要,因为信息在类型本身中。 optional<optional<...>>

专门针对 flatten_impl<nullopt_t> 是不正确的,因为您无法通过 transform 运行它。我认为没有必要。

这是一个简化版本,也演示了您可以转换类型。 它也是 constexpr!

#include <assert.h>
#include <boost/hana/core/is_a.hpp>
#include <boost/hana/core/tag_of.hpp>
#include <boost/hana/flatten.hpp>
#include <optional>
#include <string>
namespace hana = boost::hana;

struct my_optional_tag { };

namespace boost::hana {

    template <typename T>
    struct tag_of<std::optional<T>> {
        using type = my_optional_tag;
    };

    template <>
    struct transform_impl<my_optional_tag> {
        template <typename Opt,typename F>
        static constexpr auto apply(Opt&& o,F&& f) {
            using ResultValue = decltype(std::forward<F>(f)(std::forward<Opt>(o).value()));
            using Result = std::optional<std::decay_t<ResultValue>>;
            return o.has_value() ? Result(std::forward<F>(f)(std::forward<Opt>(o).value())) :
                                   Result(std::nullopt);
        }
    };

    template <>
    struct flatten_impl<my_optional_tag> {
        template <typename X>
        static constexpr auto apply(X&& x) {
            using Nested = decltype(std::forward<X>(x).value());
            static_assert(is_a<my_optional_tag,Nested>,"must have nested optional");
            return std::forward<X>(x).value_or(Nested(std::nullopt));
        }
    };
}


int main() {
    // Functor
    static_assert(hana::transform(std::optional<int>{41},[](int x) { return x + 1.0f; }) ==
            std::optional<float>{42});

    // Monad
    static_assert(hana::flatten(std::optional<std::optional<int>>{3}) == std::optional<int>{3});

    constexpr auto inc = [](int x) { return std::optional{x + 1.0f}; };
    static_assert(hana::chain(std::optional<int>{5},inc) == std::optional{6.0f});

    static_assert(hana::flatten(std::optional<std::optional<int>>{std::nullopt}) == std::nullopt);

    // hana::flatten(std::optional<int>{3}); // compile-time error
}

https://godbolt.org/z/7KWGn6

请注意,我省略了 hana::lifthana::ap 的实现,它们是 hana::Applicative 所必需的,而 hana::Monad 是必需的(在记录的 minimal complete definition )。我会把这个留给读者。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?