標高+1m

Don't be rational.

biwascheme call/cc 非同期処理と大域脱出

引き続き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