구문 파라미터

구문 파라미터 (Syntax Parameters)

구문 파라미터(syntax parameter)는 매크로 확장 과정에서 재정의할 수 있는 파라미터예요. 여기서 다루는 바인딩은 racket/baseracket이 아니라 racket/stxparam 라이브러리가 제공해요.

출처: Racket Reference

본문

(require racket/stxparam)  ; package: base

이 섹션에서 다루는 바인딩은 racket/baseracket이 아니라 racket/stxparam 라이브러리가 제공해요.

(define-syntax-parameter id expr)

id를 구문 파라미터(syntax parameter)로서의 구문으로 바인딩해요. expr은 변환기 환경(transformer environment)에서의 표현식으로, 구문 파라미터의 기본 값 역할을 해요. 이 값은 보통 변환기가 syntax-parameter-value로 얻어요.

idsyntax-parameterize나 (변환기 안에서) syntax-parameter-value와 함께 사용할 수 있어요. expr이 인자 하나를 받는 프로시저나 make-set!-transformer 결과를 만들어내면 id를 매크로로 사용할 수 있어요. exprmake-rename-transformer 결과를 만들어내면, id는 대상 식별자의 사용으로 확장되는 매크로로 사용할 수 있지만, id에 대한 syntax-local-value는 대상의 값을 만들어내지 않아요.

예제:

> (define-syntax-parameter current-class #f)
> (define-syntax-parameter yield (make-rename-transformer #'abort))
> (define-syntax-parameter define/public
    (λ (stx)
      (raise-syntax-error #f "use of a class keyword not in a class" stx)))
> (begin-for-syntax (displayln (syntax-parameter-value #'current-class)))
#f
> (yield 5)
5
(syntax-parameterize ([id expr] ...) body-expr ...+)

splicing-syntax-parameterize도 함께 보세요.

iddefine-syntax-parameter로 구문 파라미터에 바인딩되어야 해요. 각 expr은 변환기 환경에서의 표현식이에요. body-expr들을 확장하는 동안 각 expr의 값이 해당 id에 바인딩돼요.

expr이 인자 하나를 받는 프로시저나 make-set!-transformer 결과를 만들어내면, 그 idbody-expr들을 확장하는 동안 매크로로 사용할 수 있어요. exprmake-rename-transformer 결과를 만들어내면, id는 대상 식별자의 사용으로 확장되는 매크로로 사용할 수 있지만, id에 대한 syntax-local-value는 대상의 값을 만들어내지 않아요.

예제:

> (define-syntax-parameter abort (syntax-rules ()))
> (define-syntax forever
    (syntax-rules ()
      [(forever body ...)
       (call/cc (lambda (abort-k)
         (syntax-parameterize
          ([abort (syntax-rules () [(_) (abort-k)])])
          (let loop () body ... (loop)))))]))
> (define-syntax-parameter it (syntax-rules ()))
> (define-syntax aif
    (syntax-rules ()
      [(aif test then else)
       (let ([t test])
         (syntax-parameterize ([it (syntax-id-rules () [_ t])])
           (if t then else)))]))
(define-rename-transformer-parameter id expr)

id를, 반드시 make-rename-transformer 결과로 바인딩되어야 하는 구문 파라미터로서의 구문으로 바인딩해요. define-syntax-parameter와 달리, id에 대한 syntax-local-value는 대상의 값을 만들어내며, syntax-parameterize 안에서도 마찬가지예요.

예제:

> (define-syntax (test stx)
    (syntax-case stx ()
      [(_ t)
       #`#,(syntax-local-value #'t)]))
> (define-syntax one 1)
> (define-syntax two 2)
> (define-syntax-parameter not-num
    (make-rename-transformer #'one))
> (test not-num)
#<procedure:syntax-parameter>
> (define-rename-transformer-parameter num
    (make-rename-transformer #'one))
> (test num)
1
> (syntax-parameterize ([num (make-rename-transformer #'two)])
    (test num))
2

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

12.5.1 구문 파라미터 검사

(require racket/stxparam-exptime)  ; package: base
(syntax-parameter-value id-stx) → any
  id-stx : syntax?

이 프로시저는 변환기 환경에서 쓰기 위한 것이며, id-stx는 일반 환경에서 구문 파라미터에 바인딩된 식별자예요. 결과는 syntax-parameterize 형식으로 조정된 구문 파라미터의 현재 값이에요.

이 바인딩은 보통 변환기에서 쓰이기 때문에 racket/stxparam이 for-syntax로 제공해요. 정상적으로는 racket/stxparam-exptime이 제공해요.

(make-parameter-rename-transformer id-stx) → any
  id-stx : syntax?

이 프로시저는 변환기에서 쓰기 위한 것이며, id-stx는 구문 파라미터에 바인딩된 식별자예요. 결과는 id-stx처럼 행동하지만 syntax-parameterizesyntax-parameter-value와 함께 쓸 수 없는 변환기예요.

make-parameter-rename-transformer를 쓰는 것은, 파라미터를 호출하는 프로시저를 정의하는 것과 유사해요. 그런 프로시저는 파라미터 값에 접근할 수는 있지만 파라미터 값을 바꾸지는 못하게 하면서 다른 사람에게 내보낼 수 있어요. 마찬가지로 make-parameter-rename-transformer는 구문 파라미터를 매크로로 사용할 수는 있게 하지만 바꿀 수는 없게 해요.

make-parameter-rename-transformer의 결과는 make-rename-transformer의 결과와 달리 syntax-local-value가 특별히 취급하지 않아요.

이 바인딩은 보통 변환기에서 쓰이기 때문에 racket/stxparam이 for-syntax로 제공해요. 정상적으로는 racket/stxparam-exptime이 제공해요.

더 알아보기

  • 스플라이싱 구문 파라미터화: splicing-syntax-parameterize
  • 파라미터(Parameters)
  • The Racket Guide의 구문 파라미터(Syntax Parameters) 장