Gauche > Archives > 2026/03/25

2026/03/25 14:38:29 UTCkaki
#
ERIマクロを試用していて気になる挙動を発見したので報告します。
#
;; answer を束縛し、マクロ使用環境での answer へのgetterを定義
(define-syntax define-hidden-var
  (eri-macro-transformer
   (^ (form rename id=? inject)
     (match form
       ((_ getter expr)
        (let1 hidden 'answer
          (quasirename rename
            `(begin
               (define ,hidden
                 ,expr)
               (define (,getter)
                 ,(inject hidden))))))))))

(let ((answer 'outer))
  (define-hidden-var foo 42)
  (print (foo))) ; outer

(let ((answer 'outer))
  (define dummy 'dummy) ; 追加
  (define-hidden-var foo 42)
  (print (foo))) ; 42
#
dummy defineを挿入すると挙動が変わります。通常の(マクロ定義環境への)renameの場合、renameされた識別子がマクロ展開時点ではまだ束縛されていない同じinternal definitionの変数を参照できるので、後者が期待される挙動かと思います。
#
ERマクロで同様の問題が起きないか考えましたが、その場合マクロ定義とマクロ使用が同じinternal definitionに並ぶので、dummy defineで挙動が変わるといったことは無さそうです。
#
(let ((answer 'outer))
  (define-syntax define-hidden-var/rename
    (er-macro-transformer
     (^ (form rename id=?)
       (match form
         ((_ getter expr)
          (let1 hidden 'answer
            (quasirename rename
              `(begin
                 (define ,hidden
                   ,expr)
                 (define (,getter)
                   ,(rename hidden))))))))))
  (define-hidden-var/rename foo 42)
  (print (foo))) ; 42
2026/03/25 14:46:30 UTCkaki
#
あとこれはER/ERI共通の話ですが、マクロ展開中にrenameした識別子をdescribe(あるいは (slot-ref id 'env) )すると挙動が変わる(上記例だとouterになる)のもちょっと気になりました。
2026/03/25 15:45:49 UTCkaki
#
その他気付きとしては、マクロからERマクロ定義を含んだ出力をすることで、ERマクロだけでinject手続きに似たrename手続きが手に入りそうです。マクロを生成するマクロになるので、injectで書けた方がすっきりはします。
2026/03/25 16:53:58 UTCshiro
#
ERIマクロ変換器は多分途中まで作って完成させてないので、今の挙動は当てにならないです。syntax-caseで (lambda (x) (syntax-case x () ... (datum->syntax x <symbol>))) とやるのと同じ挙動になるのが意図です。
#
ERマクロは、マクロ使用環境で参照したい識別子は基本的に生シンボルを挿入することになってますが、マクロがネストするかして生シンボルが意図せずシャドウされることかなんかがあって、datum->syntaxなら回避できるけど同じことやるにはinjectが必要だよね、って流れがあったように記憶してます。ERマクロだけでできれば意味的にはすっきりしますが、できるのかな?
2026/03/25 17:09:16 UTCkaki
#
(define-syntax define-hidden-var
  (er-macro-transformer
   (^ (form rename id=?)
     (match form
       ((_ get expr)
        (let1 hidden 'answer
          (quasirename rename
            `(begin
               (define ,hidden
                 ,expr)
               (define-syntax %inject
                 (er-macro-transformer
                  (^ (form inject id=?)
                    (match form
                      ((_ id)
                       (inject id))))))
               (define (,get)
                 (%inject ,hidden))))))))))

(let ((answer 'outer))
  (define-hidden-var foo 42)
  (print (foo))) ; 42
#
こんな感じでそれっぽいrename手続きinjectが手に入りますが、ちゃんと同じかは分かりません。
2026/03/25 17:20:04 UTCkaki
#
なんかSRFI 211が、ERマクロで生シンボルを出力するとなんやかんやの問題があるからwrapしろみたいな話をしていたような: https://srfi.schemers.org/srfi-211/srfi-211.html