구문 객체 바인딩

구문 객체 바인딩 (Syntax Object Bindings)

이 섹션은 구문 객체의 식별자들이 어떤 바인딩에 연결되는지 검사하고 비교하는 절차들을 다뤄요. 매크로를 작성할 때 식별자가 동일한 바인딩을 가리키는지, 지역·모듈·최상위 바인딩 중 어떤 것인지 판별하는 데 핵심적인 함수들이에요.

출처: Racket Reference

본문

(bound-identifier=? a-id b-id [phase-level]) → boolean?
  a-id : syntax?
  b-id : syntax?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)

식별자들이 phase-level이 나타내는 단계 수준의 적절한 표현식 문맥에 치환되었을 때 a-idb-id를 바인딩하면(또는 그 반대) #t, 아니면 #f를 반환해요. phase-level#f 값은 라벨 단계 수준에 대응해요.

예제:

> (define-syntax (check stx)
    (syntax-case stx ()
      [(_ x y)
       (if (bound-identifier=? #'x #'y)
           #'(let ([y 'wrong]) (let ([x 'binds]) y))
           #'(let ([y 'no-binds]) (let ([x 'wrong]) y)))]))
> (check a a)
'binds
> (check a b)
'no-binds
> (define-syntax-rule (check-a x) (check a x))
> (check-a a)
'no-binds
(free-identifier=? a-id b-id [a-phase-level b-phase-level]) → boolean?
  a-id : identifier?
  b-id : identifier?
  a-phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)
  b-phase-level : (or/c exact-integer? #f) = a-phase-level

a-idb-id가 각각 a-phase-levelb-phase-level이 나타내는 단계 수준에서 같은 지역 바인딩, 모듈 바인딩, 또는 최상위 바인딩에 접근하면—아마 rename 변환기를 통해서—#t를 반환해요. a-phase-level 또는 b-phase-level#f 값은 라벨 단계 수준에 대응해요.

"같은 모듈 바인딩"은 식별자들이 같은 원래 정의 위치를 가리킨다는 뜻이고, 반드시 같은 requireprovide 위치를 가리킨다는 뜻은 아니에요. requireprovide의 재명명이나 변환기의 rename 변환기 바인딩 때문에, 식별자들은 syntax-e로 서로 다른 결과를 반환할 수 있어요.

예제:

> (define-syntax (check stx)
    (syntax-case stx ()
      [(_ x)
       (if (free-identifier=? #'car #'x)
           #'(list 'same: x)
           #'(list 'different: x))]))
> (check car)
'(same: #<procedure:car>)
> (check mcar)
'(different: #<procedure:mcar>)
> (let ([car list])
    (check car))
'(different: #<procedure:list>)
> (require (rename-in racket/base [car kar]))
> (check kar)
'(same: #<procedure:car>)
(free-transformer-identifier=? a-id b-id) → boolean?
  a-id : identifier?
  b-id : identifier?

(free-identifier=? a-id b-id (add1 (syntax-local-phase-level)))과 같아요.

(free-template-identifier=? a-id b-id) → boolean?
  a-id : identifier?
  b-id : identifier?

(free-identifier=? a-id b-id (sub1 (syntax-local-phase-level)))과 같아요.

(free-label-identifier=? a-id b-id) → boolean?
  a-id : identifier?
  b-id : identifier?

(free-identifier=? a-id b-id #f)과 같아요.

(check-duplicate-identifier ids) → (or/c identifier? #f)
  ids : (listof identifier?)

ids의 각 식별자를 목록의 다른 모든 식별자와 bound-identifier=?로 비교해요. 어떤 비교든 #t를 반환하면 중복 식별자 중 하나(중복인 ids의 첫 번째)를 반환하고, 아니면 #f를 반환해요.

(identifier-binding id-stx [phase-level top-level-symbol? exact-scopes?])
  → (or/c 'lexical #f
          (list/c module-path-index? symbol?
                  module-path-index? symbol?
                  exact-nonnegative-integer?
                  phase+space-shift? phase+space?)
          (list/c symbol?))
  id-stx : identifier?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)
  top-level-symbol? : any/c = #f
  exact-scopes? : any/c = #f

phase-level이 나타내는 단계 수준(여기서 #f 값은 라벨 단계 수준에 대응)에서 id-stx의 바인딩에 따라 세 종류(top-level-symbol?#f일 때) 또는 네 종류(top-level-symbol?이 참일 때)의 값 중 하나를 반환해요.

  • id-stx가 지역 바인딩을 가지면 결과는 'lexical이에요.

  • id-stx가 모듈 바인딩을 가지면 결과는 일곱 항목의 목록이에요: (list from-mod from-sym nominal-from-mod nominal-from-sym from-phase import-phase+space-shift nominal-export-phase).

    • from-mod는 정의 모듈을 나타내는 모듈 경로 인덱스예요(컴파일된 모듈과 참조 참고). 바인딩이 id-stx를 둘러싼 모듈의 정의를 가리키면 "self" 모듈 경로 인덱스예요.
    • from-sym은 원래 모듈의 해당 정의 위치에서 식별자 이름을 위한 심볼이에요. 이는 여러 이유로 syntax->datum이 반환하는 지역 이름과 다를 수 있어요: 식별자가 가져올 때 재명명되었거나, 내보낼 때 재명명되었거나, 바인딩 위치가 매크로 호출로 만들어져 묵시적으로 재명명되었기 때문이에요. 마지막 경우에는 읽을 수 없는 심볼일 수 있고, 원래 소스 정의의 식별자에 대한 syntax->datum 결과와 다를 수 있어요.
    • nominal-from-modid-stx 주변 소스에 지역적으로 나타나는 바인딩 모듈을 나타내는 모듈 경로 인덱스예요(컴파일된 모듈과 참조 참고): 그 바인딩을 제공하기 위해 id-stx의 문맥으로 요구되는 모듈을 나타내거나, id-stx를 둘러싼 모듈의 정의를 가리키는 바인딩에 대해 from-mod와 같은 "self"예요. nominal-from-mod에서 어떤 가져온 식별자를 재내보내기 때문에 from-mod와 다를 수 있어요. 같은 바인딩이 여러 방식으로 가져와지면 임의의 대표가 선택돼요.
    • nominal-from-symid-stx 주변 소스에 지역적으로 나타나는 바인딩 식별자를 위한 심볼이에요: nominal-from-mod가 내보내는 식별자 이름이거나, id-stx를 둘러싼 모듈 안 정의의 소스 식별자 심볼이에요. from-modnominal-from-mod가 같더라도 재명명 provide 때문에, 또는 매크로 확장이 도입한 정의 때문에 from-sym과 다를 수 있어요.
    • from-phase는 원래 단계를 나타내는 정확한 음이 아닌 정수예요. 예를 들어 정의가 for-syntax면 1이에요.
    • import-phase+space-shift는 바인딩 가져오기가 정의나 일반 require에서 온 것이면 0, for-syntax 가져오기에서 온 것이면 1, for-space 가져오기에서 온 것이면 단계와 공간 이름의 결합 등이에요.
    • nominal-export-phase+space는 가져온 바인딩에 대해 nominal-from-mod에서 내보낸 단계 수준과 바인딩 공간이거나, id-stx를 둘러싼 모듈의 바인딩에 대해 정의의 단계 수준이에요.
  • id-stx가 최상위 바인딩을 가지면 결과는 (list top-sym) (단, top-level-symbol?이 참일 때)이에요. top-sym은 바인딩 정의가 매크로 호출로 만들어졌을 때 syntax->datum이 반환하는 이름과 다를 수 있어요.

  • id-stx가 최상위 바인딩을 가지면(top-level-symbol?#f일 때) 또는 id-stx가 바인딩되지 않았으면 결과는 #f예요. 바인딩되지 않은 식별자는 보통 최상위 바인딩이 변수인 식별자와 똑같이 취급돼요.

id-stx가 rename 변환기에 바인딩되면, identifier-binding의 결과는 변환기 안의 식별자에 대한 것이므로, identifier-bindingfree-identifier=?와 일관돼요.

exact-scopes?가 참 값이면, id-stx의 바인딩이 정확히 id-stx의 스코프를 가질 때가 아니면 결과는 #f예요. 정확한 스코프 검사는 예를 들어 식별자가 특정 정의 문맥에 이미 바인딩되어 있는지 감지하는 데 유용해요.

변경 사항: base 패키지 6.6.0.4 버전에서 최상위 바인딩 정보를 보고하는 top-level-symbol? 인자가 추가됐어요. 8.2.0.3 버전에서 단계 결과가 단계–공간 결합으로 일반화됐어요. 8.6.0.9 버전에서 exact-scopes? 인자가 추가됐어요.

(identifier-transformer-binding id-stx [rt-phase-level])
  → (or/c 'lexical #f
          (listof module-path-index? symbol?
                  module-path-index? symbol?
                  exact-nonnegative-integer?
                  phase+space-shift? phase+space?))
  id-stx : identifier?
  rt-phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)

(identifier-binding id-stx (and rt-phase-level (add1 rt-phase-level)))과 같아요.

base 패키지 8.2.0.3 버전에서 단계 결과가 단계–공간 결합으로 일반화됐어요.

(identifier-template-binding id-stx)
  → (or/c 'lexical #f
          (listof module-path-index? symbol?
                  module-path-index? symbol?
                  phase+space? phase+space-shift? phase+space?))
  id-stx : identifier?

(identifier-binding id-stx (sub1 (syntax-local-phase-level)))과 같아요.

base 패키지 8.2.0.3 버전에서 단계 결과가 단계–공간 결합으로 일반화됐어요.

(identifier-label-binding id-stx)
  → (or/c 'lexical #f
          (listof module-path-index? symbol?
                  module-path-index? symbol?
                  exact-nonnegative-integer?
                  phase+space-shift? phase+space?))
  id-stx : identifier?

(identifier-binding id-stx #f)과 같아요.

base 패키지 8.2.0.3 버전에서 단계 결과가 단계–공간 결합으로 일반화됐어요.

(identifier-distinct-binding id-stx wrt-id-stx
                             [phase-level top-level-symbol?])
  → (or/c 'lexical #f
          (list/c module-path-index? symbol?
                  module-path-index? symbol?
                  exact-nonnegative-integer?
                  phase+space-shift? phase+space?)
          (list/c symbol?))
  id-stx : identifier?
  wrt-id-stx : identifier?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)
  top-level-symbol? : any/c = #f

(identifier-binding id-stx phase-level top-level-symbol?)과 같지만, id-stx의 바인딩이 wrt-id-stx의 스코프의 부분집합인 스코프를 가지면 결과는 #f예요. 즉 id-stxwrt-id-stx가 같은 기호 이름을 가진다면, 바인딩이 wrt-id-stx에도 적용되지 않을 때만 id-stx에 대한 바인딩이 반환돼요.

base 패키지 8.3.0.8 버전에서 추가. 8.8.0.2 버전에서 top-level-symbol? 인자가 추가됐어요.

(identifier-binding-symbol id-stx [phase-level]) → symbol?
  id-stx : identifier?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)

identifier-binding과 같지만 바인딩에 대응하는 심볼을 만들어내요. 심볼 결과는 free-identifier=?인 어떤 식별자에 대해서도 같지만, free-identifier=?가 아닌 식별자에 대해서도 같을 수 있어요(즉 다른 심볼은 다른 바인딩을 의미하지만, 같은 심볼이 같은 바인딩을 의미하지는 않아요).

identifier-binding이 목록을 만들었을 때, 그 목록의 두 번째 요소가 identifier-binding-symbol이 만드는 결과예요.

(identifier-binding-portal-syntax id-stx [phase-level]) → (or/c #f syntax?)
  id-stx : identifier?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)

id-stxphase-level에서 define-syntax#%require를 통해 포털 구문(portal syntax)에 바인딩되면 포털 구문 내용이 반환돼요. id-stx를 바인딩하는 모듈은 선언되어야 하지만, 관련 단계에서 인스턴스화될 필요는 없고, identifier-binding-portal-syntax는 그 모듈을 인스턴스화하지 않아요.

base 패키지 8.3.0.8 버전에서 추가.

(syntax-bound-symbols stx [phase-level exact-scopes?]) → (listof symbol?)
  stx : syntax?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)
  exact-scopes? : any/c = #f

(identifier-binding (datum->syntax stx sym) phase-level #f exact-scopes?)#f가 아닌 값을 만들 모든 인터닝된 심볼의 목록을 반환해요. 이 프로시저는 stx의 스코프 수에 결과 목록의 길이를 더한 것에 비례하는 시간이 걸려요.

base 패키지 8.6.0.6 버전에서 추가. 8.6.0.9 버전에서 exact-scopes? 인자가 추가됐어요.

(syntax-bound-interned-scope-symbols stx [phase-level exact-scopes?])
  → (listof symbol?)
  stx : syntax?
  phase-level : (or/c exact-integer? #f) = (syntax-local-phase-level)
  exact-scopes? : any/c = #f

(identifier-binding ((make-interned-syntax-introducer sym) stx) phase-level #f exact-scopes?)#f가 아닌 값을 만들 수 있는 인터닝된 스코프의 sym 이름 목록을 반환해요. 이 프로시저는 stx의 스코프 수에 결과 목록의 길이를 더한 것에 비례하는 시간이 걸려요.

base 패키지 8.13.0.8 버전에서 추가.

(syntax-bound-phases stx) → (listof (or/c exact-integer? #f))
  stx : syntax?

(syntax-bound-symbols stx phase-level)가 비어 있지 않은 목록을 만들 수 있는 모든 단계 수준을 포함하는 목록을 반환해요.

예제:

> (syntax-bound-phases #'anything)
'(2 1 0)
> (require (for-meta 8 racket/base))
> (syntax-bound-phases #'anything)
'(9 8 2 1 0)

base 패키지 8.6.0.8 버전에서 추가.

더 알아보기

  • free-identifier=?, bound-identifier=?
  • 컴파일된 모듈과 참조(module path index)
  • datum->syntax, syntax-e