引き続きbiwaschemeで呪文を唱えております。
;;treasure hunt (let ((data (<o> 'data (fetch-json "treasure-map.json")))) (begin (listen-click "button") (console-log (<o> 'treasure (fetch-json (<o> 'treasure-url data))))))
#0: 宝探し
call/ccで非同期処理を書くと、あらゆるところから処理が湧き出して流れていく、不思議なコードが書ける。 書けるのは良いが、実行すると、最初にcall/cc自体の値が継続の流れの中で引っかかってしまう。
(define (fetch-json%% url) (call/cc (lambda (k) (ajaxy-fetchy-thing (lambda (data) (k data))))) (define (listen-click%% sel) (call/cc (lambda (k) (add-handler! sel "click" k))))
#1: 疑似コード
つまり、非同期リクエストのレスポンスを流したい川に、
まず最初に(call/cc (lambda (k) ...))
自体の評価値(i.e. lambdaの値)が流れて行ってしまう。
そこで、lambdaの評価時はコールスタックを吹き飛ばしてしまって、継続をスキップしよう。
(define (fetch-json% url) (call/cc (lambda (k) (ajaxy-fetchy-thing (lambda (data) (k data))) (raise "Let's blow away that bustard stack frame"))) (define (listen-click% sel) (call/cc (lambda (k) (add-handler! sel "click" k) (raise "Let's blow away that bustard stack frame"))))
#2: 例外による大域脱出
うまく動くがこれでは少し荒っぽすぎるし、うるさい。
継続がない継続を使えば、例外を起こさず安全にスタックを消し去れる。 こういうのを大域脱出という。
(define idle #f) (call/cc (lambda (k) (set! idle k))) (define (fetch-json url) (call/cc (lambda (k) (ajaxy-fetchy-thing (lambda (data) (k data))) (idle))) (define (listen-click sel) (call/cc (lambda (k) (add-handler! sel "click" k) (idle))))
#3: 継続による大域脱出
なるほど。さすがはcall/cc
、制御フローのスイスアーミーナイフ。
コード中の主要な未定義関数はこちら: https://gist.github.com/ympbyc/dfc0c72956f1cecc32cd643486017a71