【发布时间】:2020-03-05 14:24:22
【问题描述】:
我想要 C++20 中的 Functor 概念。
函子是一种可以被映射的更高种类的类型。一个简单的例子是std::optional;使用从A 类型到B 类型和std::optional<A> 的函数,您可以轻松地创建std::optional<B>,方法是将该函数应用于该值(如果存在),否则返回一个空的optional。这个操作在 Haskell 中称为fmap。
template<typename A, typename B>
std::optional<B> fmap(std::function<B(A)> f, std::optional<A> fa) {
if (!fa) {
return std::optional<B>{};
}
return std::optional<B>(f(*fa));
}
一个适用于所有函子的概念很容易编写。我想出了这个(使用 GCC——你必须删除 bool 才能让它在 Clang 中工作,我认为):
template<template<typename> typename F, typename A, typename B>
concept bool Functor = requires(std::function<B(A)> f, F<A> fa) {
{ fmap(f, fa) } -> F<B>;
};
还有一个简单的附加功能来确保它有效:
template<typename A, typename B>
std::function<B(A)> constant(B b) {
return [b](A _) { return b; };
}
template<template<typename> typename F, typename A, typename B>
F<B> replace(B b, F<A> fa) requires Functor<F,A,B> {
return fmap(constant<A,B>(b), fa);
}
它有效。但这并不漂亮。我想要的是replace 的签名是这样的:
template<Functor F, typename A, typename B>
F<B> replace(B b, F<A> fa);
这里不需要 requires 子句。好多了,你不同意吗?然而,要让它发挥作用,我必须将我的概念模板简化为一个参数。像这样的:
template<template<typename> typename F>
concept bool Functor = requires(function<B(A)> f, F<A> fa) { // Uh-oh
{ fmap(f, fa) } -> F<B>;
};
问题是,我没有声明类型 A 和 B。据我所知,在我必须使用它们之前,我无法声明它们。我想做的事能做到吗,能不能简单优雅地完成?
我想到的一个可能的解决方案是使概念中的 requires-expression 成为模板(或至少是类似模板的东西)。然后我会有这样的事情:
template<template<typename> typename F>
concept bool Functor = requires<typename A, typename B>(function<B(A)> f, F<A> fa) {
{ fmap(f, fa) } -> F<B>;
};
很遗憾,这在 C++20 标准中是无效的,并且无法使用 g++-8 进行编译。这样的事情是否可行?能不能成为标准?
【问题讨论】:
-
@NicolBolas C++ 没有这样的概念。有些人在表示“函数对象”时会使用“函子”这个词。 functor 和 monad 是不同的,但相关的东西。
-
@Barry:“C++ 没有这样的概念。”是的,有。只需谷歌“C++ 函子”,您就会发现数以千计的匹配项,其中没有一项符合 Haskell 的定义。它可能没有被标准定义(这就是我在“概念”周围使用引号的原因),但它是 C++ 社区中的一个艺术术语,具有非常具体的定义。
-
@NicolBolas “它可能没有被标准定义”——事实上,这使它成为一个没有意义的艺术术语,当我们有一个完全合理的替代品时,我们应该鼓励人们停止使用它“功能对象。”特别是因为函子在 FP 世界中具有更有用和更具体的含义。
-
@Barry:“这使它成为一个没有意义的艺术术语” 整个 C++ 社区都不同意,所以我会接受他们的评估。如果你想与人交流,你必须使用他们理解的术语,而不是试图强迫他们遵循你的定义。 “特别是因为函子在 FP 世界中具有更有用和更具体的含义。” C++ 不是函数式语言,因此它不需要遵循函数式命名约定。
-
@Barry:人们知道“函数对象”是什么意思。但 C++ 程序员也认为“函子”是“函数对象”的缩写。如果您开始与他们交谈,就好像这些是不同的术语,他们将无法理解您在说什么。不同的社区用词不同;最好接受这一点并继续前进。
标签: c++ templates c++20 c++-concepts