式の評価を遅延させることが有用な場合があります。例えば、プログラムの後続処理で結果が不要であることが判明した場合に、時間のかかる計算の実行を避けたい場合などが挙げられます。thunk ライブラリでは、このような遅延評価をサポートするために、以下の関数とマクロを提供しています。
forms を評価するための サンク
を返します。サンクとは、thunk-delay の呼び出しのレキシカル環境を継承するクロージャ(クロージャ を参照)のことです。このマクロを使用するには lexical-binding が必要です。
thunk を強制し、thunk を生成した thunk-delay
で指定されたフォームの評価を行わせます。最後のフォームの評価結果が返されます。また、thunk
は自身が強制されたことを「記憶」しており、同じ thunk を用いて thunk-force をさらに呼び出しても、フォームを再評価することなく同じ結果が返されるだけとなります。
このマクロは let と同様ですが、「遅延」変数バインディングを生成します。任意のバインディングは
(symbol value-form) という形式をとります。let とは異なり、任意の value-form
の評価は、forms の評価時に対応する symbol
のバインディングが初めて使用されるまで延期されます。任意の value-form
は最大で1回のみ評価されます。このマクロを使用するには lexical-binding が必要です。
例:
(defun f (number)
(thunk-let ((derived-number
(progn (message "Calculating 1 plus 2 times %d" number)
(1+ (* 2 number)))))
(if (> number 10)
derived-number
number)))
(f 5) ⇒ 5
(f 12) ⊣ Calculating 1 plus 2 times 12 ⇒ 25
lazily bound 変数はその特殊な性質から、これらに値を代入すること(例:setq)はエラーとなります。
これは thunk-let と同様ですが、bindings 内の任意の式は、この thunk-let*
形式における先行する束縛を参照できます。このマクロを使用するには lexical-binding
が必要です。
(thunk-let* ((x (prog2 (message "Calculating x...")
(+ 1 1)
(message "Finished calculating x")))
(y (prog2 (message "Calculating y...")
(+ x 1)
(message "Finished calculating y")))
(z (prog2 (message "Calculating z...")
(+ y 1)
(message "Finished calculating z")))
(a (prog2 (message "Calculating a...")
(+ z 1)
(message "Finished calculating a"))))
(* z x))
⊣ Calculating z...
⊣ Calculating y...
⊣ Calculating x...
⊣ Finished calculating x
⊣ Finished calculating y
⊣ Finished calculating z
⇒ 8
thunk-let と thunk-let*
は暗黙的にサンクを使用します。これらの展開によってヘルパーシンボルが生成され、それらが束縛式をラップするサンクにバインドされます。その後、本体 forms 内の元の変数へのすべての参照が、対応するヘルパー変数を引数として thunk-force を呼び出す式に置き換えられます。したがって、thunk-let や thunk-let*
を使用するコードはすべてサンクを使うように書き換えることが可能ですが、多くの場合、これらのマクロを使用した方が、サンクを明示的に使用するよりも洗練されたコードになります。