我认为你能做的最接近的是提供一个特定的功能,A --> Monad<B>
并验证它是否做正确的事情。为了防止无限递归,我们可以验证它是否A --> M
有效:
template <class M>
concept bool Monad()
{
return requires(M m) {
{ m >>= std::function<M(ValueType<M>)>{} } -> M;
};
}
这只是一种特定情况,但我认为无法验证通用情况是否A --> Monad<X>
有效,因为概念检查仍然涉及特定表达式,您只能创建具有特定类型的特定表达式。
当然,我们可以提供多个这样的要求。使用重新绑定元函数:
template <class M, class X>
struct rebind;
template <class M, class X>
using rebind_t = typename rebind<M, X>::type;
template <template <class...> class Z, class R, class X>
struct rebind<Z<R>, X> {
using type = Z<X>;
};
然后我们可以为返回各种类型的函数添加要求,假设它也适用于int
s:
template <class M>
concept bool Monad()
{
return requires(M m)
{
{ m >>= std::function<M(ValueType<M>)>{} } -> M;
{ m >>= std::function<rebind_t<M,int>(ValueType<M>)>{} } -> rebind_t<M,int>;
};
}
通过将其重构为自己的子概念,这可能会变得更容易:
template <class M, class R>
concept bool MonadBind()
{
return requires(M m) {
{ m >>= std::function<rebind_t<M,R>(ValueType<M>)>{} } -> rebind_t<M,R>;
};
}
template <class M>
concept bool Monad()
{
return requires(M m) {
requires MonadBind<M, ValueType<M>>();
requires MonadBind<M, int>();
};
}