Identifiers that appear in <literals> .. とは?

<リテラル>の扱いがいまいちわからない部分があったので整理します。

4.3 Macros

...
4.3.2 Pattern language

A <transformer spec> has the following form:
syntax: syntax-rules <literals> <syntax rule> …
...

Identifiers that appear in <literals> are interpreted as literal identifiers to be matched against corresponding subforms of the input. A subform in the input matches a literal identifier if and only if it is an identifier and either both its occurrence in the macro expression and its occurrence in the macro definition have the same lexical binding, or the two identifiers are equal and both have no lexical binding.

Revised⁵ Report on the Algorithmic Language Scheme
— Richard Kelsey, William Clinger, And Jonathan Rees (Editors)

後者(or〜以降)は、二つの識別子が同じで、両方とも字句的束縛がされていない、ということは、例えば、 yelse の束縛が無い場合に以下のような結果になる、ということだと考えられます。

(let-syntax
    ((test (syntax-rules (yelse)
             ((_ yelse) 'ok)
             ((_ x) 'no))))
  (test yelse))
;;=> ok

前者がどんな例があるかを考えます。素直に考えると以下のものが思い付くのだけれども、上の例との違いがほとんど無いので、いまいちはっきりとしません。これをもってして、“扱いがいまいちわからない”、と考えています。

(the macro expression って?というのもあるのですが、以下の場合では、多分、 (test yelse) の式のことだと思うのですが自信無いです…)

(let ((yelse '(value)))
  (let-syntax
      ((test (syntax-rules (yelse)
               ((_ yelse) 'ok)
               ((_ x) 'no))))
    (test yelse)))
;;=> ok

自分なりに考えを整理すると、見た目・字面が同じなので、単に字面で <literals> を比較しているのでは?という疑問が残る、ということです。

この疑問を解消するには、(1)それぞれ異なる場所に束縛されている識別子を比べてみる、(2)どちらか一方のみが、ある場所に束縛されている識別子を比べてみる、その結果、リテラルのパターンにマッチしないということを確認すれば納得できると考えたので、やってみました。

例えば、マクロの使用している外側で <literals> に現れる識別子を別の場所に束縛して試すことはよくあります。仕様にものっている例だと以下のように。

(let ((=> #f))
  (cond (#t => 'ok)))
;;=> ok

ただ、 => を評価すると、シンタックスが返ってくるので特別扱いなのか?とか別の疑問が出てきてしまう…

話を戻して。以下の2つ、 'no が返ってくることが期待されていると考えます。

「それぞれ異なる場所に束縛されている識別子を比べてみる」もの: 'no が返ってくると考えます。

(let ((yelse '(value)))
  (let-syntax
      ((test (syntax-rules (yelse)
               ((_ yelse) 'ok)
               ((_ x) 'no))))
    (let ((yelse #f))
      (test yelse))))
;;=> no ;; 正しい結果だと考えられます

この場合、どうやら単に字面を比べているのではない、ということがわかります。次に。

「どちらか一方のみが、ある場所に束縛されている識別子を比べてみる」もの: まず、マクロを呼び出している所では、ある場所に束縛されている場合。やっぱり 'no が返ってくると考えます。

(let-syntax
    ((test (syntax-rules (yelse)
             ((_ yelse) 'ok)
             ((_ x) 'no))))
  (let ((yelse '(value)))
    (test yelse)))
;;=> no ;; 正しい結果だと考えられます

一方、マクロ定義で <literals> に現れる識別子が、ある場所に束縛されている場合、というのが簡単には書き下ろしずらいです。以下のような、別のモジュールから呼び出すという方法を思い付きました。

Gauche の場合では別のモジュールから字面を変えて呼び出せば納得できる結果を得ることができると考えられます。

(select-module user)

(define-module t.syntax-rules-test
  (export-all))
(select-module t.syntax-rules-test)

(define yelse '(value))

(define-syntax test-literal-bound
  (syntax-rules (yelse)
    ((_ yelse) 'ok)
    ((_ _) 'no)))

(test-literal-bound yelse)
;;=> ok
;; これだけだと字面で比較しているかもしれないという疑問が残ります

(define-syntax test-literal-unbound
  (syntax-rules (zelse)
    ((_ zelse) 'ok)
    ((_ _) 'no)))

(test-literal-unbound zelse)
;;=> ok

(let ((zelse '(value)))
  (test-literal-unbound zelse))
;;=> no
;; これは仕様にもあるものによく似ています:
;; (let ((=> #f))
;;   (cond (#t => 'ok)))
;; => ok

(select-module user)

(import (t.syntax-rules-test :prefix t:))

(t:test-literal-bound t:yelse)
;;=> ok
;; 同じ場所に束縛されている識別子なので 'ok が返る(字面が違うのに注目します)
;; ただし、インポートを上記のように使うとこの結果が*たまたま* 'ok が返るよう
;; に識別子がインポートされる、ということの裏返し、とも考えられます。
;; つまり、これは実装に依存する動作かもしれない、と。

(let ((yelse t:yelse))
  (t:test-literal-bound yelse))
;;=> no
;; 異なる場所に束縛されている識別子なので 'no が返る(字面は同じ)

;; XXX: これがわからない
(t:test-literal-bound yelse)
;;=> ok
;; マクロ呼び出しの `yelse` はどの場所にも束縛されていない。 一方で、
;; マクロ定義の `yelse` は束縛されているので 'no だと考えられるけれども…

マクロ定義の <literals> に現れる識別子が束縛されている場合で、マクロを呼び出す時には束縛が見えない場合、というのは思い付いた。

(let-syntax
    ((test (syntax-rules ()
             ((_ arg)
              (let ((yelse '(value)))
                (let-syntax ((test-aux
                              (syntax-rules (yelse)
                                ((_ yelse) 'ok)
                                ((_ x) 'no))))
                  (test-aux arg)))))))
  (test yelse)) ;; ここでは `yelse` は束縛されていない
;;=> ok ;; 正しい?

わからないので質問してみよう。