deferred.el であそんでみる
deferred:loop を接続したいと思っていろいろやってみた。
ためしみた
最初に書いたのがこれ。
(deferred:$ (deferred:loop (loop for x from 0 to 1000 collect x) (lambda () (message "%s" x))) (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda () (message "%s" x))) (deferred:nextc it (lambda () (message "end"))))
うまくいかない。最初のloopと次のloopが一緒に走ってる感じ。次に deferred:nextc で loop を囲んでみた。
(deferred:$ (deferred:loop (loop for x from 0 to 1000 collect x) (lambda (x) (message "%s" x))) (deferred:nextc it (lambda () (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda (x) (message "%s" x))))) (deferred:nextc it (lambda () (message "end"))))
これだとうまくいった。deferred:loop は前の deferred と接続できないから、deferred:nextc で開始を遅延させる。
しらべてみた
微妙に不思議なのが 2回目の loop が終わってから "end" ってメッセージがでること。2回目の loop がはじまったらそのまま deferred:nextc が終わってすぐに "end" って出るんじゃないかと思った。
そこで deferred:nextc の実装を見てみた。deferred:nextc は簡単。 callback を呼び出す deferred を作って 引数の d と繋ぐだけ。
(defun deferred:nextc (d callback) "Create a deferred object with OK callback and connect it to the given deferred object." (let ((nd (make-deferred :callback callback))) (deferred:set-next d nd)))
ここから呼ばれてる deferred:set-next で目的の処理をしてた。deferred:exec-task の返り値が deferred ならそれを返り値としてる。
(defun deferred:set-next (prev next) "[internal] Connect deferred objects." ... (let ((ret (deferred:exec-task next 'ok (deferred-value prev)))) (if (deferred-p ret) ret next)) ...)
RAEDME の「実行時に接続」とか「lambdaの返り値に気を使う」は多分これのこと。
やっとなっとくした。
もうちょっとあそんでみた
まだほとんど使ってないけど、deferred:$ を使うときって大体 deferred:nextc で接続するんじゃないかと思う。そう思って引数の関数を deferred として順次実行する deferred:sequential ってのを作ってみた。
(defun deferred:sequential (&rest args) (deferred:loop args 'funcall))
これでさっきの loop のやつはこんな風に書ける。少しみやすくなった。
(deferred:sequential (lambda () (deferred:loop (loop for x from 0 to 1000 collect x) (lambda (x) (message "%s" x)))) (lambda () (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda (x) (message "%s" x)))) (lambda () (message "end")))
lambda を書くのが面倒ならこんなのを定義するのもいいかもしれない。
(defmacro deferred:$$ (&rest elements) (lexical-let ((elements elements)) `(deferred:sequential ,@(loop for i in elements collect `(lambda () ,i)))))
これを使うと lambda を書かなくてよくなる。やりすぎっぽけど。
(deferred:$$ (deferred:loop (loop for x from 0 to 1000 collect x) (lambda (x) (message "%s" x))) (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda (x) (message "%s" x))) (message "end"))
いろいろためしてみたけど
test-deferred.el を見るのが一番はやく理解できるんじゃないかと思った。というわけで deferred.el を使いたい人は test-deferred.el を見るのがいいと思います。