Gauche:LazyParameter
(Shiro 2018/12/05 04:31:39 UTC):
元々は、rfc.tlsのdefault-tls-classパラメータの実装で困ったのが発端。rfc.tlsはバックエンドとしてaxTLSとmbedTLSをサポートしていて、configure時に選べる。default-tls-classは、どちらのサポートも無ければ#fに、axTLSのサポートが入っていれば<ax-tls>に、axTLSが無くてmbedTLSが入っていれば<mbed-tls>に初期化される。
ところで、mbedTLSのサポートはライセンスの関係上バイナリを分けておきたい。そこで、mbedTLSに関わる部分はrfc.tls.mbedというモジュールに切り離し、rfc.tls内で<mbed-tls>をautoload設定している。(use rfc.tls)して<mbed-tls>を参照した途端、rfc.tls.mbedがautoloadされるという仕掛けだ。
ここで、default-tls-classを<mbed-tls>で初期化する部分が問題になる。rfc.tls中で
(cond-expane ... mbedTLSだけがサポートされている場合 ... (define default-tls-class (make-parameter <mbed-tls>)) )
などとやってしまうと、default-tls-classを定義しようとした時点で<mbed-tls>が評価され、autoloadがトリガされてしまう。 rfc.tls.mbedは(use rfc.tls)しているので、rfc.tls中にrfc.tlsをuseすることになり、循環依存でエラーになる。
次のとおり<mbed-tls>の評価を遅延すれば循環依存は避けられるが:
(define default-tls-class (make-parameter (delay <mbed-tls>)))
この場合、(default-tls-class)はPromiseを返すので、いちいちforceしてやらねばならない。使う側が「このパラメータはforceしないとならない」って気にするのは抽象化の漏れだし、既にdefault-tls-classを使ってるところを書き直さなければならない。
そこで、「参照時に自動的にforceする」という属性をパラメータに持たせることを考えた。
Promiseを返したいパラメータもあるだろうから、デフォルトは今まで通りで、作成時にLAZY_PARAMETERフラグを渡したら値を取り出す時にforceする。
この機能はrfc.tlsの例だけでなく、一般に「グローバルな値を計算で生成したいが、重い処理なので本当に必要な時まで計算を遅らせたい」という用途に使える。delay/forceは排他制御をしてくれるので、他のスレッドとの競合を気にする必要もない。