I think the closest thing you can do is to provide a specific function A --> Monad<B>and make sure that it does the right thing. To prevent infinite recursion, we can just make sure that it A --> Mworks:
template <class M>
concept bool Monad()
{
return requires(M m) {
{ m >>= std::function<M(ValueType<M>)>{} } -> M;
};
}
This is only one specific case, but I don’t think it is possible to check the general case when it works A --> Monad<X>, since concept checks are still associated with certain expressions, and you can only create specific expressions with certain types.
, . :
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>();
};
}