Haskell の Cont モナドを C++ で表現しようとしていますが、タイピングを C++ に変換する際に混乱しています。
私の現在のアプローチは、bind 操作と return 操作用に 2 つのクラスを作成することです。これらは、値の型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
。
質問
元のコードに基づいて、2 つの質問があります。
R
との型はA
正しいBindCont
ですか。両方に明示的な型と型を追加R
しようとしましたが、それらを正しく取得できないようです。A
BindCont
ReturnCont
BindCont::Run
異なるタイプの計算を一緒にフックするためにどのように実装できますか?
編集
Run
また、 returnvoid
を使用すると元のケースで機能し、bind を正しく実装できるようになることにも注意してください。これは特定のケースでは受け入れられるかもしれませんが、戻り値を持つことが望ましいです。