補助構文のrename
(import (prefix (rnrs) rnrs.)) (rnrs.define-record-type (foo make-foo foo?) (rnrs.fields (rnrs.mutable brr))) (make-foo 1)
のようなコードが正常に動作しないというバグ(rnrs.fieldsが正常にrenameされないように振る舞い、fieldsを代わりに使うことで動作する)。原因自体は非常に単純で:
; Searches for a clause beginning with the given symbol, ; returning the entire clause (as a syntax object) if found ; or #f if no such clause is found. (define (clauses-assq sym clauses) (syntax-case clauses () (((x1 x2 ...) y ...) (if (and (identifier? #'x1) (eq? sym (syntax->datum #'x1))) #'(x1 x2 ...) (clauses-assq sym #'(y ...)))) ((y0 y1 y2 ...) (clauses-assq sym #'(y1 y2 ...))) (x #f))) ... (fields-clause (clauses-assq 'fields clauses))
Larcenyから移植してきたR6RS recordの実装が、syntax→datumしたうえでeq?でシンボルと比較している部分があるため。clauses-assqは、syntax-caseな手続きの内部で、clauseを収集するために使用される。eq?で比較すると構文情報が抜け落ちてしまうため、renameが効かなくなる。本来はfree-identifier=?的に比較する必要がある。
例えば、
(library (check) (export check foo) (import (rnrs)) (define-syntax foo (lambda (p) 'invalid)) (define-syntax check (lambda (p) (define (proc x) (display (list 'eq?: (eq? 'foo (syntax->datum x)) 'id?: (free-identifier=? #'foo x))) (newline) #t) (syntax-case p () ((_ sym) (proc #'sym) (syntax "Do nothing")))))) (import (check)(rnrs)) (check foo)
のようなコードを書いて、(check)ライブラリからfooをexportしたりしなかったりすることでこれらの挙動を観察できる。