環境フレームモデル

置き換えモデルではbeginやset!が絡んだ時に正しい値にならなかった。だけど、環境フレームモデルでは正しく計算できるらしい。
だい〜ぶお利口、置き換えモデルよりいくらかCOOL(by 宮崎吐夢


環境フレームモデルでも「環境」を用いるが、置き換えモデルのそれとはちょっと違う。

環境フレーム
置き換えモデルで出てきた「環境」と同じ。束縛の表。
環境
環境フレームモデルでは、複数の環境フレームを用いるが、環境フレーム全体のこと。

では、どのようにして複数の表を使うのだろうか。

環境フレームモデルでは、

  • 関数適用((lambda (arg) body) param)にさしかかったら
    1. 新たに作成した環境フレームにargとparamからなる束縛を追加し、その環境フレームを環境にプッシュ
    2. 上記で拡張した環境でbodyを評価
    3. プッシュした環境フレームをポップ
  • defineで、現在の環境フレームに束縛を追加

という感じ。環境フレームモデルも作用順序評価なのは同じ。
名前を探さなくちゃいけない時は、新しい方の環境フレームから探していって、最初に見つかった束縛を使う。

では、本のコード例をやってみる。

(((lambda (y)
    (lambda (x)
      (begin
        (set! y (+ x y))
        y)))
  10)
 1)

リストを評価しようとしているので、関数適用。よって、

  • 引数1を評価し、整数1を取得
  • 手続きを作成

作成する手続きは以下を評価すると得られる。...(a)

((lambda (y)
    (lambda (x)
      (begin
        (set! y (+ x y))
        y)))
 10)

これも関数適用。よって、

  • 引数10を評価し、整数10を取得
  • lambda式を評価し、値としての手続き作成。
  • できた手続きを引数10に適用、つまり...
    1. 新たな環境フレーム1を作成し、そこにyと10を結び付ける束縛を追加。環境フレーム1をプッシュ。
    2. 拡張された環境の下で、(lambda (x) (begin ... ))を評価し、手続きを取得。
    3. プッシュした環境フレーム1をポップ

これで、(a)の手続きができたので、

  • 出来た手続きを引数1に適用、つまり...
    1. 新たな環境フレーム2を作成し、そこにxと1を結び付ける束縛を追加。環境フレーム2をプッシュ。
    2. 拡張された環境の下で、(begin (set! y (+ x y)) y)を評価...(b)
    3. プッシュした環境フレーム2をポップ

beginとset!と+はプリミティブなので中身はブラックボックスとしよう。とくにbeginやset!はマクロなので手続きではないので詳しい手順は分からないけど、

  1. 環境フレーム1のyに関する束縛のうち、値の方を(+ x y)の評価結果で書き換える
  2. yの値(書き換え後)を返す

ってことを、この順序で行うはずので、そういうことで納得しておく。


一つ気になる点が。
(b)の時点で、yの束縛を保持した環境フレーム(つまり環境フレーム1)は既にポップされているから、yという名前を検索しても見つからなくね?
にもかかわらず、(+ x y)や(set! y (+ x y))ちゃんと評価できるのはナゼ?
評価しようとしている式のyが環境フレーム1の束縛への参照を持っているので、まだ環境フレームは1はGCに破棄されてないから...かな?
150ページの脚注

ポップされた環境フレームは、参照がある限りずっと残ります。どこからも参照されなくなった後に、ガベージコレクタによって回収されます。

が意味するところはこういうことかな?

訂正?

マクロとはユーザ定義の構文らしいので、beginやset!はマクロとは呼ばないかもです。