我试图在 C++ 中表达 Haskell 的 Cont monad,并且对将类型转换为 C++ 感到困惑。
我目前的方法是为绑定和返回操作创建两个类。它们拥有一个值类型A
和返回类型R
。
template<typename _R, typename _A>
class Cont
{
public:
typedef _A A;
typedef _R R;
virtual R Run(const std::function<R(A)>& k) const=0;
};
template<typename M, typename F,
typename B = typename std::remove_pointer<typename std::result_of<F(typename M::A)>::type>::type,
typename R = typename B::R,
typename A = typename B::A>
class BindCont : public Cont<R, A>
{
M* m;
F f;
public:
explicit BindCont(M* m, const F& f) : m(m), f(f) { }
virtual R Run(const std::function<R(A)>& k) const
{
return m->Run([&](typename M::A x) { return f(x)->Run(k); });
}
};
template<typename A>
class ReturnCont : public Cont<A, A>
{
A x;
public:
explicit ReturnCont(A x) : x(x) { }
virtual A Run(const std::function<A(A)>& k) const
{
return k(x);
}
};
template<typename M, typename F>
BindCont<M, F>* Bind(M* m, const F& f) { return new BindCont<M, F>(m, f); }
template<typename A>
ReturnCont<A>* Return(A x) { return new ReturnCont<A>(x); }
这适用于组合相同类型的对象,但可以理解的是,当 Bind 转换为另一种类型时会失败:
// This works as the resulting Cont is the same as the Cont being bound.
auto m1 = Bind(Return(4), [](int x)
{
return (x < 10 ? Return(10) : Return(x));
});
// This fails as when m is run in BindCont, it expects an int, not a const char*
// to be returned.
auto m2 = Bind(Return(4), [](int x)
{
return (x < 10 ? Return("abc") : Return("xyz"));
});
其他尝试
我的第一次尝试只依赖并确定了来自泛型仿函数的A
返回类型:R
Run
template<typename A>
class ReturnCont : public Cont<A>
{
A x;
public:
explicit ReturnCont(A x) : x(x) { }
template<typename K>
typename std::result_of<K(A)>::type Run(const K& k) const { return k(x); }
};
然而,虽然它解决了 bind 实现的问题,但需要Cont
在运行时决定使用哪个实现,因为泛型Run
接口不能在基类上表示Cont
。
问题
根据原始代码,我有两个问题:
R
和A
的类型是否BindCont
正确?我尝试为两者添加显式R
和A
类型BindCont
,ReturnCont
但似乎无法使它们正确。- 如何
BindCont::Run
实现将不同类型的计算挂钩在一起?
编辑
我还应该注意,在原始情况下Run
返回void
有效并允许正确实现绑定。这在某些情况下可能是可以接受的,但最好有一个返回值。