首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对于C++23可选的一元绑定和一元返回是什么?

对于C++23可选的一元绑定和一元返回是什么?
EN

Stack Overflow用户
提问于 2022-01-06 11:06:55
回答 2查看 1K关注 0票数 12

C++23 std::optional终于得到了一些非常有用的补充。

由于我对FP的了解非常原始,所以我想知道以下两个操作的语法(根据我的googling是2个基本的一元操作):

  1. 一元绑定
  2. 一元回报

我最好的猜测是:

一元绑定是转换

一元回报就是C++17 std::optional 构造函数(8)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-01-06 12:02:20

mbind (它不存在,我在模仿哈斯克尔的>>=)

在类似C++的伪代码中,一元绑定(让我们称之为mbind )应该具有这样的签名:

代码语言:javascript
复制
C<U> mbind(C<T>, std::function<C<U>(T)>);

也就是说,它应该在某种类型的C上使用一个单模T,这个函数“将该单模的内部拉出来”,然后在一个(不一定)不同类型的UC<U>上变成一个单一C,并将这个C<U>返回给您。

transform (自由函数)

首先,您提到的transform是一个成员函数,它有一个签名类型

代码语言:javascript
复制
C<U> C<T>::transform(std::function<U(T)>);

但是,让我们重写它的签名,就像它是一个自由函数一样:

代码语言:javascript
复制
C<U> transform(C<T>, std::function<U(T)>);

因此,正如您所看到的,它接受一个C<T>,并将一个函数从T应用到U ,就在函子中,从而产生一个C<U>

所以有不同之处。

要更好地理解其中的区别,请尝试将transform传递给C<T>和一个带有签名的函数-- mbind所期望的函数std::function<C<U>(T)>

你得到的是啥?请记住,transform应用的函数“就在函子内部,没有拔出任何东西”,因此您将得到一个C<C<U>>,即多一个函数层。

相反,使用相同的两个参数的mbind会给出一个C<U>

您如何从transform(x, f)返回的内容转到mbind(x, f)返回的内容,即从C<C<U>>返回到C<U>?您可以通过Haskell中的flatten/join/collapse/whatever-you-want-to-name-it和其他语言中的flatten来实现这两个功能级别。

显然,如果您可以这样做(对于C = std::optional也可以),那么这些“功能层”实际上是“一元层”。

那么,有一个mbind-like的东西吗?

正如在另一个答案中所建议的,有and_then成员函数。

and_then是我前面提到的(不存在的) mbind吗?是的,它们之间唯一的区别是transform成员函数和transform空闲函数之间运行的相同:一个是成员,另一个是自由的。

顺便问一下,flatten/join在哪里?

您可能想知道C++23中是否有这个实用程序。我完全不知道,我几乎不知道C++20提供了什么。

但是,由于使std::optional成为函子的函数被定义为std::optional本身的成员函数,所以我强烈认为,如果std::optional有一元绑定函数,那么它也将被定义为成员函数,在这种情况下,它将是在这一页,在Monadic操作一节中,它与std::optional一起使用。因为没有,我倾向于假设它不存在。

但是,Haskell的一些知识可以帮助我了解为什么不需要将其添加到标准中。

如果你这么做会怎么样?

代码语言:javascript
复制
auto optOfOpt = std::make_optional(std::make_optional(3));
auto whatIsThis = optOfOpt.and_then(std::identity);

是的,就是这样:在嵌套的可选项上调用.and_then(std::identity)等同于wannabe .join()

其他图书馆呢?

Boost.HanaFunctorApplicativeMonad和许多其他概念定义了概念,为您提供了一种自动实现所有抽象概念的方法,这些抽象概念利用这些概念的代价是给出一些最小的定义。例如,如果您在transform_implflatten_impl中为std::optional定义了namespace boost::hana,则允许您在其上使用transformflattenchainap和任何其他需要单点或更少的操作。

例如,下面是工作代码(编译器资源管理器的完整示例):

代码语言:javascript
复制
    auto safeSqrt = [](auto const& x) {
        return x > 0 ? std::optional(std::sqrt(x)) : std::nullopt;
    };
    {
        auto opt = chain(std::optional(2), safeSqrt);
        std::cout << opt.value_or(-1) << std::endl; // prints sqrt(2)
    }
    {
        auto opt = chain(std::optional(-2), safeSqrt);
        std::cout << opt.value_or(-1) << std::endl; // prints -1
    }
    {
        auto opt = chain(std::nullopt, safeSqrt);
        static_assert(std::is_same_v<decltype(opt), std::nullopt_t>); // passes
    }

(1)最初我更强调and_then不是mbind,因为前者是成员函数,后者是自由函数。

我之所以强调这一点,是因为成员函数“属于”类(也就是说,如果没有类的成员,就不可能有一个成员函数),所以在某种程度上,我们这里讨论的and_then与我们编写的名称函数完全无关,这个名称函数是为了使std::vector成为一个单体,因为它将驻留在std::vector中。

另一方面,非会员transform和假设的mbind是免费函数,因此它们不需要任何类,因此它们看起来更像一些类型可以选择的一般抽象的接口(比如Haskell的类型类)。显然,假设std::transformmbind是定制点,希望选择某种类型的客户端代码必须为该类型编写自定义,这可能会利用成员函数。

对这个问题的回答让我想起了另一个问题,所以我已经问过了

票数 12
EN

Stack Overflow用户

发布于 2022-01-06 11:40:48

不完全是。

在Haskell语法中,bind是形式m a -> (a -> m b) -> m b,它对应于满足这个概念(对于所有ABF)。

代码语言:javascript
复制
template <class Fn, class R, class... Args>
concept invocable_r = std::is_invocable_r_v<R, Fn, Args...>;

template <class Bind, template <class> M, class A, class B, invokable_r<M<B>, A> F>
concept bind = invocable_r<Bind, M<B>, M<A>, F>;

这是and_then ( this绑定到第一个参数)。transform是fmap ( this绑定到第二个参数),它是函子操作(所有单子都是函子)。

fmap的形式是(a -> b) -> f a -> f b

代码语言:javascript
复制
template <class Fmap, template <class> M, class A, class B, invokable_r<B, A> F>
concept fmap = invocable_r<Fmap, M<B>, M<A>, F>;

不同之处在于被绑定或映射的函数的返回类型。

这种区别的另一个例子是.NET的linq Select vs SelectMany

另一个棘手的问题是,monad规则讨论的是表达式,而不是语句,因此必须将构造函数包装在函数中。

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70606173

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档