しかし、周囲で見られるほとんどのWebフレームワークは、頑固にこの考えを無視しています。
継続を使用して、RESTful(ステートレス)Webアプリケーションをより便利で使い慣れたステートフル形式に魔法のように変換します。
本質的に、「セッション」はWebアプリケーションの実行状態を保存するように設計されています。
そして、その使用はデータセットとして愚かです-単に言語のフル機能の継続の欠如のために。
継続は、言語レベルでのアプリケーションの状態です。
スキームでは、最初のクラスのオブジェクトとしてサポートされています-それらは関数から返され、関数のパラメーターとして渡され、最終的に関数として呼び出されます。
継続を呼び出すと、アプリケーションは継続が作成された場所に戻り、そこから実行を続けます。
さらに、これは複数回行うことができます。
ある意味では、これらは例外ですが、その反対は真実です。
プログラムのある時点で例外が発生し、ハンドラーがインストールされている外部のどこかに制御を移します。
継続はプログラムのある時点で発生し、この時点に制御を戻すことができます。 この場所のプログラムは、そうだったと見なします。
たとえば、Pythonでは、これはyieldステートメントによって実装されます。
yieldを含む関数は「イテレータージェネレーター」と見なされ、その実行は最初のyieldステートメントで終了し、関数はnext()を使用して次の反復を呼び出すことができるイテレータータイプオブジェクトを返し、(values)を送信してyieldの遷移ポイントに値を返します。
next()およびsend()メソッドでイテレータを呼び出すと、next、next、valueが返されます。
イテレータは数値を反復するために使用できます(フィボナッチ数などの数値が無限にある場合は信じられないほど楽しいかもしれません)、ツリーを走査して、 すべてを反復できます 。
同様に、イテレータはWeb番号やツリーノードではなく、Webページを生成できます。
たとえば、リソースを表示してショッピングカートに入れる関数は次のようになります。
browse_stuff(): # criteria = yield(ask_criteria()) # results = find_stuff(criteria) # , - yield (list_stuff(results)) # raise StopIteration() def buy_stuff(continuation, item) if not user.authentificated: # , , user = yield(login_form(user)) # , , # , quantity = yield(ask_quantity()) # , delivery = yield(ask_delivery()) # money = yield(ask_payment()) # status = launch_order(item,quantity,delivery,money) # "" yield(message(" . .")) # , ( browse_stuff) continuation.next() # , " " ?
継続パラメーターは、「購入不要」リンクを使用して遷移が行われた場所でのアプリケーションの状態(別の機能の実行)です。
このシナリオのメインアプリケーションは、イテレータのように見えます。
cont = browse_stuff_iter()
contオブジェクトはセッションまたは他の場所に保存され、クライアントがアクセスするたびに復元されます。
または、いくつかのオブジェクトが保存され、それらの識別子がURLにエンコードされ、非表示のフォームフィールドに配置されるため、ユーザーが戻るボタンをクリックすると、ユーザーは前のページを表示するだけでなく、実際にはアプリケーションの前の状態に戻ります。
Cont.next()はGETリクエストで呼び出されます
Cont.send(フォームデータ)はPOSTリクエストで呼び出されます
これらの呼び出しの結果はページとして表示されます。
このメソッドを使用すると、buy_stuff()のようにフォームのチェーンを生成できます
新しいリンクをクリックすると、これは中断と見なされ、現在の状態がハンドラに渡されます:buy_stuff(cont、item)
更新:
Python、PCP、Javaのどちらのテンペラでも、ファーストクラスオブジェクトのレベルでは、継続に対する本格的なサポートがないことに注意してください。
Pythonのyieldは非常に似た振る舞いをし、アイデアを説明するために使用されました。
実際にどのように機能するかは完全には明らかではありません。
継続の完全なサポートは、 さまざまなエキゾチックな言語で利用可能ですが、Rubyでも同様です。
Scheme、Lisp、Smalltalk、OCaml、およびJavaScript用の拡張サーバーがいくつかあります。
コメントのどこかで、PHP用のシンプルなオファーエミュレーターへのリンクが失われました。
ウェブの未来、明確に-言語のために、本格的な継続をサポートします!
特に、継続を第一級のオブジェクトとしてサポートする言語で、他の構成要素(データの構造化、カプセル化、継承の構成要素を含む)がまったく必要ないことを思い出してください-それらはすべて継続によって実装されています(特別な場合として、機能的です)回路))
さて、そして、それが通常起こるように、そのような考えはすでにより構造化された連続的な方法で表現されています:)
未使用の文献のリスト:
mega-UPD:スキームの図
コードの正確さを保証できません。 しかし、括弧はバランスが取れています:)
;; サーバーコード ((uri-> cont uri)「URIからどこかから継続を取得する」を定義する) (define(cont-> uri cont) "継続をどこかに保存し、そのURIを形成します) (define(insert-uri tmpl uri)「テンプレートにリンクを挿入」) (define(render-page tmpl args) "htmlでページをレンダリングします") (define(handle-request request)「継続を呼び出し、要求を渡す」 (if(eqv?(get-uri request)init-uri)); 開始URIのリクエストの場合 (開始); スタートを切る ((uri-> cont(get-uri request))request)));; そうでなければ-続き (定義(make-response cont template)「テンプレート:リンクまたはフォームのあるページ」。 (render-page(insert-uriテンプレート(cont-> uri cont))))) ;; サーバー終了コード ;; アプリケーションコード (定義(クエストルーム) (定義(解析要求要求)「フォームデータまたはコードを解析し、選択肢を返す」) (定義(ウォークオン選択)「現在と選択によって新しい部屋を選択します」) (定義(ページ取得)「現在のルームのHTMLテンプレートを返す」) (define(response cc)(make-response cc(get-page))「愚かなパラメーターのカリー化」) (quest(walk-on(parse-request(call / cc response))))))) ;; 初期化コード (init-uri "/ mytextquest"を定義) (define(start)(quest 'start-room))
ここで何が起こるか(起こるはずです)
0.部屋を特定するパラメーターでクエストが呼び出されます。
0.5唯一の呼び出し-最後の、最も極端なパラメーター-式(呼び出し/ cc(応答))
1. call / cc(これは特別な構成要素です)関数を呼び出します(応答の継続)
2.順番に-呼び出し(応答作成継続テンプレート)
3. make-responseは、この継続を識別するURIをテンプレートに(たとえば、アクションフィールドや遷移リンクに)挿入します。
4.レンダリングされたページ
5.ユーザーがページ上のリンクのいずれかをクリックするか、フォームを送信します
6. handle-requestはリンクをプルして続行します
7.そして、リクエストパラメータでそれを呼び出します
8. call / ccが立っている場所から実行が継続されます。 要求は、呼び出し/ cc呼び出しの結果として置換されます
9.リクエストが解析され、ユーザーがそこに突っ込んだロジックを取得します
10.ウォークオンは、ユーザーが今入る部屋を計算します
11.クエストは、次の部屋を識別するパラメーターで再帰的に呼び出されます
部屋に加えて、ユーザーのカルマがパスの選択に影響する場合。 ポケットの中身。 そして周囲の生態系の状態-これらはすべて「部屋」にカプセル化されています。
ユーザーが「戻る」を押して前のURIに切り替えると、前の状態が引き出されて実際に前の部屋に入り、遷移の影響を受けるすべての動物が生き返ります