見出し画像

Retro-gaming and so on

ポール・グレアムのスコープの解説

実の事を言うと、もう既に我々はダイナミック・スコーピングに馴染みがない。
ダイナミック・スコーピングに馴染んでるのは、旧い、ANSI Common Lisp以前のLispに馴染んでる層か、あるいは現役でEmacs Lispをプログラムしてる層くらいしかいないだろう。
ダイナミック・スコーピングのLispはいくつか生き残ってるが(たとえば往年のSUN Mycrosystemsが開発したnewLispとか)、多分ユーザーは殆どいない。
そもそも、PascalやC言語以降、レキシカルスコーピング、あるいは静的スコープ、と言うのが「あまりにもフツーに」なってしまっているのだ(※1)。

ところで、ポール・グレアムはこのスコーピングに付いてOn Lispで軽い記述で解説している。
しかし、その挙げられた例は、Scheme系言語をやってる層にはワケワカメである。少なくとも僕は、初見、「こんな書き方が成り立つのか?」とビックリしてた。
ポール・グレアムはこんなコードを紹介してた。

(let ((y 7))
 (defun scope-test (x)
  (list x y)))

マジで「何だこれは?」とか思った当時の僕(笑)。「letdefunを囲む、だって???」と。「こんなんが出来るのか?」と。
ちなみに、Racketでこのコードを移植しようとするとエラーになる。



他のScheme処理系でも、ポール・グレアムが挙げてる例

(let ((y 5))
 (scope-test 3))
  (3 5)

を実行しようとするとエラーを喰らうだろう。
たとえば次はChicken Schemeの例だ。



GuileでもRacketと似たようなエラーが出る。



RacketとGuileが何に怒ってるのか、と言うと「defineみたいなマクロを式とは認めないよ」と言う事だが、Chicken Schemeは「scope-testなんて関数は定義されてないじゃない」と怒ってる。
ここで注目したいのはChecken Schemeが返してるエラーメッセージだ。
これは当然で、「letで包まれている以上、中で定義したscope-testは外部からアクセスは不可だ」と言う事だ。
そう、実はSchemeの厳密なレキシカル・スコープの適用から考えるとscope-testletの外側からは「見えない」。言い換えると「エラーが出てるからこそ」レキシカル・スコープになってる、って事の証明になってるんだ。
しかし、ANSI Common Lispは、言い換えると、defunの存在自体がレキシカルスコープのルールに反してる(笑)。そう、letに包まれてようが何だろうが、お構いなしにdefunは大域関数を定義すると言うスコープに反した作業が出来る為、ポール・グレアムが書いたような(Schemeだと全く役に立たない)例が作れるんだよな。

実はRacketで考えると、ポール・グレアムが書いた例は次のようなモノだ。

(define scope-test #f)

(let ((y 7))
 (set! scope-test
  (lambda (x) `(,x ,y))))

(let ((y 5))
 (scope-test 3))

つまり、一旦大域変数としてscope-testを定義しておいて(ここで#fを与えてるが実のトコ、なんでも良い)、(let ((y 7)) ... 以下で set!scope-testをラムダ式で再束縛してしまう。
そうすれば、ラムダ式が定義された環境を閉じ込めたクロージャが、外部のscope-testに束縛されて、大域変数scope-testが関数化する。
これで目出度くscope-testは大域環境で認識されるので、(let ((y 5)) ... 内でもアクセス出来るようになるわけだ。



これでポール・グレアムが例示したようなレキシカルスコープの実験が出来た。
なお、ポール・グレアムが暗示してるが、レキシカル・スコープとレキシカル・クロージャと言うのは密接な関係がある。と言うか、レキシカル・スコープを厳密に実装すると、レキシカル・クロージャがもれなくついて来る、って考えてまぁ良いとは思う。スコープとクロージャの決定的な違い、ってのは、それを意図的に取り出して自在に使えるか使えないか、って程度の差しかない(それがデカいんだけどね)。

一方、ダイナミック・スコーピングの実験は、単純にはGNU Emacsを頼ればいいだろう。何度か言ってるが、GNU Emacsに搭載されているEmacs Lispは古典的なダイナミック・スコープのLispだ。
実はユーザーからは「レキシカル・スコープにして欲しい」と言う要請が結構あるみたいだが、オリジナルの製作者、リチャード・ストールマンがレキシカル・スコープ嫌いな模様で(笑)、それでその案は廃案になってるらしい。
どうしてもEmacs Lispをレキシカル・スコープにしたい、と言う人たちは現在ではCLパッケージを用いてる模様だ。
閑話休題。GNU Emacsの*scratch*バッファを用いるか、あるいはEshellを用いれば、ダイナミック・スコープの場合、レキシカル・スコープとは挙動が違う、と言う事が分かる。



まぁ、正直な事を言うと、別にダイナミック・スコープを「理解せねばならない」と言うこたぁねぇとは思うんだけどね。
2020年代の現代、だとダイナミック・スコープを採用しているプログラミング言語はほぼないので、マイナーな話題だ、って印象は避けられないし、しかもそりゃ当然だろう、って思ってる。
言い換えると、前回扱ったfluid-letとか、あるいはポール・グレアムの解説とかは、クッソ古いLispを実際使った事がある層じゃないと思いつかん例だ、って事は言えるたぁ思う。
もっと言うと「おっさんの悪あがき」である(笑)。

※1: しかしながら、Pythonはこの辺割にいい加減な実装である。PythonとJavaScriptを比べると、JavaScriptの方が「マトモだ」とは言えるだろう。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「プログラミング」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事