구문 파라미터
구문 파라미터 (Syntax Parameters)
구문 파라미터(syntax parameter)는 매크로 확장 과정에서 재정의할 수 있는 파라미터예요. 여기서 다루는 바인딩은 racket/base나 racket이 아니라 racket/stxparam 라이브러리가 제공해요.
출처: Racket Reference
본문
(require racket/stxparam) ; package: base
이 섹션에서 다루는 바인딩은 racket/base나 racket이 아니라 racket/stxparam 라이브러리가 제공해요.
(define-syntax-parameter id expr)
id를 구문 파라미터(syntax parameter)로서의 구문으로 바인딩해요. expr은 변환기 환경(transformer environment)에서의 표현식으로, 구문 파라미터의 기본 값 역할을 해요. 이 값은 보통 변환기가 syntax-parameter-value로 얻어요.
id는 syntax-parameterize나 (변환기 안에서) syntax-parameter-value와 함께 사용할 수 있어요. expr이 인자 하나를 받는 프로시저나 make-set!-transformer 결과를 만들어내면 id를 매크로로 사용할 수 있어요. expr이 make-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도 함께 보세요.
각 id는 define-syntax-parameter로 구문 파라미터에 바인딩되어야 해요. 각 expr은 변환기 환경에서의 표현식이에요. body-expr들을 확장하는 동안 각 expr의 값이 해당 id에 바인딩돼요.
expr이 인자 하나를 받는 프로시저나 make-set!-transformer 결과를 만들어내면, 그 id는 body-expr들을 확장하는 동안 매크로로 사용할 수 있어요. expr이 make-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-parameterize나 syntax-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) 장