興味深い callbacksとcontinuationsの違いについての議論 over on SOがこの質問を促しました。定義により、継続は、計算を完了するために必要なロジックの抽象的な表現です。ほとんどの言語では、これは継続的な処理が必要な値を渡します。
純粋に関数型の言語(すべての関数が純粋でファーストクラスの市民である)では、継続は完全に関数としてモデル化できると思います。結局のところ、これは私がこれまでに継続を理解してきた方法です。ただし、世界は状態(ため息)でいっぱいなので、一般的な定義では、継続キャプチャプログラムの状態は必要ありません。
私の理解を助けるために、継続が関数より抽象的な方法で表現される関数型言語で例を提供できますか? Schemeは現在の継続をファーストクラスの方法(call/cc)で取得できることを知っていますが、それでも、call/ccに渡された1つの引数のプロシージャには現在の継続が別の形式で与えられているようですcall/cc'd関数がその結果を適用できる引数手続き。
tl; dr; typeは継続に対する包括的な抽象化です
非手続きベースの継続に最も近いものは、型として表現される Haskellの継続モナド である可能性が高く、多くの関数を使用して、型と対話して中断することができます。再開、バックトラックなど。
そのクロージャーをHaskellのCont
タイプなどのタイプにカプセル化すると、モナドの抽象化を「より高いレベルの抽象化」として取得できます。また、たとえば、単に手続きの代わりにtypeとして継続
結局のところ、あなたは基本的に正しいです。継続は「手続き」ですが、私はむしろそれをクロージャーと呼んでいます。多くの場合、継続は、バインドされた環境を囲んでいるファーストクラスのクロージャとして最もよく表現されます。純粋な関数型言語では、参照がないため、これは特に合理的ではないと言えます。これは真実ですが、値を囲むことができ、単一の割り当てにより、値と参照を同じように囲むことができます。これはHaskellで生じます:
(\x -> \y -> insideYIcanAccess x (and y))
バインディング環境を囲む機能がない言語は、技術的にはファーストクラスのクロージャーを欠いている可能性がありますが、それでもクロージャーで使用できるsome環境(通常はグローバル)があります。
したがって、継続を次のように説明する方が正確だと思います:特定の方法で使用されているクロージャー
「継続は手続き以外の方法で実装可能ですか?」の質問には、ありません。もし ファーストクラスがない場合functions あなたはそれ自体継続を実際に持つことはできません(はい、関数ポインタはファーストクラスの関数としてカウントされるため、任意のメモリアクセスで十分です)。
「手続きよりも抽象的な方法で継続を表現する方法はありますか?」を型として表現すると、非常に優れた抽象化により、継続を非常に一般的な方法で処理できるため、継続を実行するだけでなく、より多くの方法で継続と対話できます。
あなたが好きかもしれない一例はコルーチンです。たとえば、LuaのコルーチンまたはPythonまたはC#のイテレータ/ジェネレータは、ワンショットの継続(1回しか呼び出せない継続)と同じように機能しますが、継続は明示的に関数にしたかわりに、次の "yield"ステートメントまでコルーチンを進める方法があります。
たとえば、次のPythonプログラムを考えてみます。
def my_iterator():
yield 1
yield 2
yield 3
def main():
it = my_iterator()
x = it.next()
y = it.next()
z = it.next()
print x + y + z
これは、明示的なコールバックを備えた次のJavascriptプログラムに似ています。
function my_iterator()
return function(cb1){
cb1(1, function(cb2){
cb2(2, function(cb3){
cb3(3, function(cb4){
throw "stop iteration";
});
});
});
});
}
function main(){
var getNext1 = my_iterator();
getNext1(function(x, getNext2){
getNext2(function(y, getNext3){
getNext3(function(z, getNext4){
console.log(x + y + z);
});
});
});
}
JavaScriptの例は、生成された値(Python