4

对不起,如果问题不是太清楚。我不确定表达它的最佳方式(随意编辑!)。我认为一个例子是最清楚的:

我试图根据Haskell 定义定义一个 Monad 概念。绑定运算符 ( >>=) 要求 aMonad类型A可以绑定到一个接受A并返回 aMonad类型的函数B。我可以A根据value_typetypedef 进行定义,但是如何B在我的概念中定义类型?

template <typename M>
concept bool Monad()
{
  return requires(M m, Function<_1, ValueType<M>> f) {
    // (>>=) :: m a -> (a -> m b) -> m b
    { m >>= f } -> M
  }
}

在上面的例子中,我用什么来代替_1Function<> 概念中的?

这是否也足以将调用 f 的结果限制为任何类型的 Monad?

4

1 回答 1

1

我认为你能做的最接近的是提供一个特定的功能,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>;
};

然后我们可以为返回各种类型的函数添加要求,假设它也适用于ints:

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>();
    };
}
于 2016-04-12T18:33:35.883 回答