obj が識別子、すなわち識別子をあらわす構文オブジェクトの場合 #t を返し、それ以外は #f を返す。
identifier? は、フェンダー中で入力の特定の部分フォームを検査するのに使うことが多い。 以下の、自己参照型のオブジェクトを作成する rec の定義等がその例である。
(define-syntax rec (lambda (x) (syntax-case x () [(_ x e) (identifier? #’x) #’(letrec ([x e]) x)]))) (map (rec fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1)))))) ’(1 2 3 4 5)) ⇒ (1 2 6 24 120) (rec 5 (lambda (x) x)) ⇒ &syntax exception
手続き bound-identifier=? と free-identifier=? は引き数に識別子をふたつとり、それが等価であれば #t を返し、さもなくは #f を返す。 これらの述語は識別子が与えられた文脈でその識別子が自由参照であるか束縛変数であるものとして、その使用意図にしたがって比較をおこなう。
id1 と id2 は識別子でなければならない。手続き bound-identifier=? は、引き数の識別子の参照が束縛のスコープ内にあらわれた場合に、変換子の出力中で、一方がもう一方への参照を捕捉する場合、かつまたその場合にかぎり #t を返し、そうでなければ #f を返す。 一般に、ふたつの識別子が bound-identifier=? であるのは、両方がもとのプログラムにあらわれるか、両方が同一の変換子適用で(おそらくは暗黙のうちに。datum->syntax 参照)挿入された場合だけである。 実際的には、ふたつの識別子が bound-identifier=? であるとされるのは、同一の名前をもち、同一のマークがつけられている場合だけである(R6RS:翻訳:Standard Libraries:12.1 Hygiene)。
bound-identifier=? は、束縛構文で識別子の重複を検出したり、束縛構文の前処理で束縛済みの識別子の検出をする場合に使うことができる。
id1 と id2 は識別子でなければならない。手続き free-identifier=? は、引き数の識別子両方が変換子の挿入した束縛の外側で変換子の出力にあらわれ、ふたつが同一の束縛に帰着する場合、かつまたその場合にかぎり #t を返す(同名の識別子がどちらも束縛に帰着しない場合、すなわちどちらも未束縛の場合、ふたつは同一の束縛に帰着したと考える)。 実際的には、ふたつの識別子が free-identifier=? であるとされるのは、いちばん上層にある置換が同一の束縛を指しているか(R6RS:翻訳:Standard Libraries:12.1 Hygiene)、識別子同士が同名で、対応する置換が存在しない場合だけである。
syntax-case と syntax-rules では free-identifier=? をつかってリテラルのリストにあげられている識別子と入力中の識別子を比較する。
(let ([fred 17]) (define-syntax a (lambda (x) (syntax-case x () [(_ id) #’(b id fred)]))) (define-syntax b (lambda (x) (syntax-case x () [(_ id1 id2) #‘(list #,(free-identifier=? #’id1 #’id2) #,(bound-identifier=? #’id1 #’id2))]))) (a fred)) ⇒ (#t #f)
以下の named-let 部分をはぶいた let の定義では bound-identifier=? を使って識別子の重複を検出している。
(define-syntax let (lambda (x) (define unique-ids? (lambda (ls) (or (null? ls) (and (let notmem? ([x (car ls)] [ls (cdr ls)]) (or (null? ls) (and (not (bound-identifier=? x (car ls))) (notmem? x (cdr ls))))) (unique-ids? (cdr ls)))))) (syntax-case x () [(_ ((i v) ...) e1 e2 ...) (unique-ids? #’(i ...)) #’((lambda (i ...) e1 e2 ...) v ...)])))
unique-ids? の引き数の #'(i ...) は、上の syntax の説明にある規則からリストになる。
この let の定義により、次の式
(let ([a 3] [a 4]) (+ a a)) ⇒ &syntax exception
例外が送出される。しかし、
(let-syntax ([dolet (lambda (x) (syntax-case x () [(_ b) #’(let ([a 3] [b 4]) (+ a b))]))]) (dolet a)) ⇒ 7
は 7 に評価される。これは、識別子 a は dolet が挿入したものであり、入力から取り出した識別子 a とは bound-identifier=? ではないからである。
次の case の定義は R6RS:翻訳:Standard Libraries:12.4 Parsing input and producing output 節の定義と同等である。 ただし、前の版では else をリテラルのリストに追加していたが、今回の版では free-identifier=? をつかって else を調べている。
(define-syntax case (lambda (x) (syntax-case x () [(_ e0 [(k ...) e1 e2 ...] ... [else-key else-e1 else-e2 ...]) (and (identifier? #’else-key) (free-identifier=? #’else-key #’else)) #’(let ([t e0]) (cond [(memv t ’(k ...)) e1 e2 ...] ... [else else-e1 else-e2 ...]))] [(_ e0 [(ka ...) e1a e2a ...] [(kb ...) e1b e2b ...] ...) #’(let ([t e0]) (cond [(memv t ’(ka ...)) e1a e2a ...] [(memv t ’(kb ...)) e1b e2b ...] ...))])))
どちらの定義でも、外側の静的束縛に else があると、else は補助キーワードとしてあつかわれない。 たとえば、
(let ([else #f]) (case 0 [else (write "oops")])) ⇒ &syntax exception
は構文エラーになる。 これは else が静的に束縛されてい、case の定義中の else と同一ではないからである。