私が考えたわけではなく、広く使われているテクニックのようだ。以下の資料の3.12に書かれている。

http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf

x86アセンブリなどに変換されるコンパイラを想定している。仮想マシン型のインタプリタにも利用されるテクニックのようだが、そちらは私はよくわからない。

前提条件

実装方法

以下の変換を行うことで変数をエミュレートすることができる。

( let (( f ( lambda ( c ) ( cons ( lambda ( v ) ( set! c v )) ( lambda () c ))))) ( let (( p ( f 0 ))) (( car p ) 12 ) (( cdr p )))) ( let (( f ( vector ( lambda ( c ) ( cons ( lambda ( v ) ( vector-set! c 0 ( vector-ref v 0 ))) ( lambda () ( vector-ref c 0 ))))))) ( let (( p ( vector (( vector-ref f 0 ) ( vector 0 ))))) (( car ( vector-ref p 0 )) ( vector 12 )) (( cdr ( vector-ref p 0 )))))

変数が導入されるのはlambda, letのみであるから、そこで導入された変数をすべてベクターで包んでやるとうまく行く。

(set! x c) => (vector-set! x 0 c) (f x y z) ;; apply => (f (vector x) (vector y) (vector z)) (let ((x a) (y b)) ...) => (let ((x (vector a)) (y (vector b))) ...) (+ x y) ;; 変数の参照 => (+ (vector-ref x 0) (vector-ref y 0))

あまりよく見ていないのだが、Gaucheでも同様の変換を行っているようだ。

Gauche/box.c at 12b71a83c6e18481e31c83d35ed42c191eb7683f · shirok/Gauche · GitHub

個人的に感動したテクニックなのでココに記しておく。参考までに私の実装例を挙げておく。

set! (6c1c503f) · Commits · t / scm-incr · GitLab