C ++中的Cont Monad

时间:2013-07-10 04:56:56

标签: c++ templates monads continuations

我试图用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


问题

根据原始代码,我有两个问题:

  • RABindCont的类型是否正确?我尝试为RA添加明确的BindContReturnCont类型,但似乎无法使其正确。
  • 如何实现BindCont::Run将不同类型的计算挂钩在一起?

修改

我还应该注意,Run返回void在原始情况下有效,并允许正确实现绑定。对于某些情况,这可能是可以接受的,但最好具有返回值。

0 个答案:

没有答案