独自の拡張

 構文キーワードは変数ではない。(実装上は変数でも良いだろうが、仕様書上の概念的には。)では変数は構文キーワードとなれるのか?これは場合による。実装依存の部分が大きい。Gaucheの場合は、式のコンパイル時に既にどの構文キーワードに束縛されているかがわかっていれば変数は構文キーワードとして使える。例えば、

(define x if)
(x (= 0 1)
   (print "a")
   (print "b"))

みたいなコードは余裕で動く。なんと、

(define x (read-line))
(if (string=? x "a")
    (define y when)
    (define y unless))
(print (y #t "when or unless?")))

という用に、ユーザの入力によってyの束縛先が変わるような場合でも動く。しかし、以下のように関数として括ってしまうとコンパイルエラーになる。

(define (z)
  (define x (read-line))
  (if (string=? x "a")
      (define y when)
      (define y unless))
  (print (y #t "when or unless?")))

(define (main args)
  (z))

これがshiroさんの言う所の「実際の処理系では、Gaucheみたいにコンパイルと実行をインターリーブするものがあって、その場合は実行時に変更した構文の意味が次のコンパイルに反映されたりするわけですが、それは処理系の拡張ということになります。」という事なのだろう。Gaucheではトップレベルの式毎にコンパイルして実行し、また次のトップレベル式をコンパイルして実行し…、と繰り返しているようだ。一気に全部の式をコンパイルしている訳ではない。

(追記):shiroさんのコメントによると、Gaucheには全部の式を一気にコンパイルするモードもついているので、オフィシャルには「実行時情報に依存するようなコードを書いてはいけない」そうです。変数に構文キーワードを束縛するようなコードを書き始めたら、注意しましょう。