3

私はJavascriptに非プリエンプティブマルチスレッドの非常に特殊な形式を追加する方法を探しています。 MozillaのJavascript 1.7では、yieldを使用してネイティブコルーチンをサポートしていますが、ブラウザ固有のソリューションを使用しないことをお勧めします。注釈付きのJavaScriptコードをプレーンなJavascriptに変換することに基づいて、継続またはコルーチンの実装がいくつかあることがわかりました。いくつかの例は、StratifiedJS,Narrative Javascriptおよびjwacsです。続きを含むJavascriptコードを生成する背後にあるトリックとは何ですか?

シミュレートされたJavascript非同期呼び出し用の完全な機能を備えたフレームワークは必要ありません。私は実装したいと思う非常に具体的な用法のためだけにそれを必要とします。したがって、上記のlibsは私にとって過度のものです。

誰かが、そのようなプリプロセッサが使用する基本的な "トリック"(またはトリック)を教えてもらえますか? Javascriptでいくつかの特別なコードを生成するという代償を払って継続を可能にする特別な言語のハックはありますか?関連する参考文献を歓迎します。

答えて

10

これは、継続通過型である。

JavascriptがLispのですが、C.

の服を身に着けている構文としてJavascriptがその中核に関数型言語なので、本当にクレイジーなトリックが継続渡しスタイルのように、可能です。しかし、これらのトリックは頭痛を誘発します。

要約すると、継続は、次に何をすべきかという概念です。関数とまったく同じように、呼び出し可能なものとして利用できます。私は時には継続を呼び出しフレームのスタックとして見ることもできます。関数呼び出しのスタックを実行状態として保存し、後でこの状態に戻すか、単に呼び出すことができます。

誰かが、継続的な通過スタイルにコードを変換することによって、継続の力を得ることができることを実証しました。うわー!それは本当に印象的です:

あなたは継続性の力を持っています。

ここで、Javascriptの問題はCの構文です。 Cの構文でソースコードを変換するのは難しいです。 Lisp構文では簡単だが、やはり面倒でエラーが起こりやすい。

本当に独創的な人たちが私たちのために努力してくれたことは幸いです。このハードワークは、Javascriptパーサーの使用を必要とします。なぜなら、この変換は実際には何を意味するのでしょうか?要約すると、最初に実際に行われたことが最初に来るように操作の順序を並べ替えることを意味します。

f(g(a + x)) 
a + x

添加は次いで、関数呼び出しg()f()、最初に行われます。サブ式は3つあります。 CPS変換では、サブ式の結果が継続に渡されます。これには、一時的な継続として多くの内部ヘルパー関数の作成が含まれます。これは、以下に示すように、複雑で面倒なことがあります。

http://en.wikipedia.org/wiki/Continuation-passing_style例関数

(define (pyth x y) 
    (sqrt (+ (* x x) (* y y)))) 

(define (pyth& x y k) 
    (*& x x (lambda (x2) 
     (*& y y (lambda (y2) 
       (+& x2 y2 (lambda (x2py2) 
          (sqrt& x2py2 k)))))))) 

に変換され、これはJavacript

function pyth(x, y) { 
    return Math.sqrt(x * x + y * y); 
} 

に対応するが、*、+とMath.sqrt()関数ではありませんCPSが理にかなっています。

しかし、*、+、Math.sqrt()がWebサービスであると仮定します。これは、Javascript Webサービスの呼び出しがの非同期であるため、重要です。非同期呼び出しを使用して作業しているすべての人は、それらの結果を結合するのがどれだけ複雑かを知っています。前処理ライブラリまたは複数のジェネレータを使用すると、非同期の結果に簡単に対処できます。

それでは、別の方法での例を書いてみましょう:

function pyth(x, y) { 
    return sqrt(add(mul(x, x), mul(y, y))); 
} 

その後、CPS変換を次のようになります。

function pyth_cps(x, y, k) { 
    mul_cps(x, x, function(x2) { 
    mul_cps(y, y, function(y2) { 
     add_cps(x2, y2, function(x2py2) { 
     sqrt_cps(x2py2, k); 
     }) 
    }) 
    }); 
} 

我々は結果のコードはインサイドアウト引き裂かと積極的に行われていることがわかり読めない。各関数が変換されます。彼らはすべて魔法のパラメータkを得る。それが継続です。 javascriptでは、操作の結果を取得する関数です。コールスタックkのどこかに深いところが呼び出されます。この例では、ここには示されていないsqrt()のCPS変換です。

CPSで変換された関数は返されません。彼らは計算の結果を続けるだけです。これは、スタックが枯渇する可能性があります。すべてのJavascript CPSトランスフォーマーがこれを処理する必要があります。スキームでは、すべての呼び出しが末尾呼び出しであるため、これは必要ではありません。テールコールは追加の呼び出しフレームを必要としません。 Javascriptでは、トランポリンまたは同様の技術が必要です。継続を直接呼び出すのではなく、ヘルパーを呼び出して結果と継続を渡します。ヘルパーは無限ループで実行され、スタックの枯渇を常に呼び出して返します。

このCPSはなぜ私たちに継続の力を与えるのですか?それは、継続は単に次のことであるからです。私たちが常に次のことを追加のパラメータkとして実行し、常に現在の式の結果を渡すなら、この概念をコードで実現しました。しかし、これまで見てきたように、これはいつも持ち歩くのが面倒です。

ソースコードのプリプロセッサに手間をかけても、手数料がかかります。なぜ継続を使うべきですか?制御フローを抽象化することは可能です。 WebアプリケーションフレームワークであるSeasideは、ブラウザのステートレスな要求フローを抽象化するために継続を使用します。ユーザーのやりとりを簡潔にモデル化することができます。ユーザーの対話は、もはや要求ではなく対話の流れで考える必要はありません。これは、継続の力の多くの例の一つにすぎません。この力はまた、多くの人々には奇妙で幾分恐ろしいようです。

+0

私はCPSをよく知っていますが、一般的な形式の命令コードをa-la-javascriptに変換するのは簡単ではありません。どのようにシーケンシャルコードからCPSに自動的に変換しますか? –

+0

最初にソースコードを解析し、最初の部分式が評価され、式の結果が継続に渡されるように式を並べ替えます。 I. http://en.wikipedia.org/wiki/Continuation-passing_styleで説明されているのと同じ操作を行いますが、Javascriptで行います。 – nalply

関連する問題