R6RS Scheme: 구문 키워드를 위한 바인딩 형식

R6RS Scheme: 구문 키워드를 위한 바인딩 형식

구문 키워드들을 바인딩하는 let-syntaxletrec-syntax 형식을 설명해요. 키워드 바인딩의 문법, 의미, 그리고 두 형식 사이의 차이를 예시로 다뤄요.

출처: 문서

본문

let-syntaxletrec-syntax 형식은 키워드들을 바인딩해요. begin 형식과 마찬가지로, let-syntax 또는 letrec-syntax 형식은 정의 맥락(definition context)에 나타날 수 있는데, 그 경우 그것은 정의로 취급되고 본문 안의 형식들도 정의여야 해요. let-syntax 또는 letrec-syntax 형식은 표현식 맥락에 나타날 수도 있는데, 그 경우 그것들의 본문 안의 형식들은 표현식이어야 해요.

(let-syntax <bindings> <form> ...) — syntax

문법: <bindings>는 다음 형식이어야 해요:

((<keyword> <expression>) ...)

<keyword>는 식별자이고, 각 <expression>은 매크로 확장 시점에 변환기(transformer)로 평가되는 표현식이에요. 변환기는 syntax-rules 또는 identifier-syntax(11.19절 참조) 또는 "syntax-case" 라이브러리 장에서 설명한 다른 메커니즘 중 하나로 만들어질 수 있어요. 바인딩되고 있는 키워드들의 목록에 <keyword>가 두 번 이상 나타나는 것은 문법 위반이에요.

의미: <form>들은 let-syntax 형식의 구문 환경을, 키워드들이 <keyword>들이고 명시된 변환기들에 바인딩된 매크로들로 확장해 얻은 구문 환경에서 확장돼요. 각 <keyword>의 바인딩은 <form>들을 그것의 지역(region)으로 가져요.

let-syntax 형식의 <form>들은 정의 맥락이든 표현식 맥락이든 암묵적인 begin으로 감싸인 것처럼 취급돼요. (11.4.7절 참조) 따라서 <form>들을 확장한 결과의 정의들은 let-syntax 형식 대신 나타나는 어떤 정의가 가지는 것과 같은 지역을 가져요.

구현 책임: 구현은 <expression>의 값이 변환기일 수 없으면 그것을 검출해야 해요.

(let-syntax ((when (syntax-rules ()
                     ((when test stmt1 stmt2 ...)
                      (if test
                          (begin stmt1
                                 stmt2 ...))))))
  (let ((if #t))
    (when if (set! if 'now))
    if))                            ⇒ now

(let ((x 'outer))
  (let-syntax ((m (syntax-rules () ((m) x))))
    (let ((x 'inner))
      (m))))                        ⇒ outer

(let ()
  (let-syntax
    ((def (syntax-rules ()
            ((def stuff ...) (define stuff ...)))))
    (def foo 42))
  foo)  ⇒ 42

(let ()
  (let-syntax ())
  5)  ⇒ 5

(letrec-syntax <bindings> <form> ...) — syntax

문법: let-syntax와 같아요.

의미: <form>들은 letrec-syntax 형식의 구문 환경을, 키워드들이 <keyword>들이고 명시된 변환기들에 바인딩된 매크로들로 확장해 얻은 구문 환경에서 확장돼요. 각 <keyword>의 바인딩은 그것의 지역 안에 <bindings>뿐만 아니라 <form>들을 가지므로, 변환기들은 letrec-syntax 형식이 도입한 매크로들의 사용으로 형식들을 전사할 수 있어요.

letrec-syntax 형식의 <form>들은 정의 맥락이든 표현식 맥락이든 암묵적인 begin으로 감싸인 것처럼 취급돼요. (11.4.7절 참조) 따라서 <form>들을 확장한 결과의 정의들은 letrec-syntax 형식 대신 나타나는 어떤 정의가 가지는 것과 같은 지역을 가져요.

구현 책임: 구현은 <expression>의 값이 변환기일 수 없으면 그것을 검출해야 해요.

(letrec-syntax
  ((my-or (syntax-rules ()
            ((my-or) #f)
            ((my-or e) e)
            ((my-or e1 e2 ...)
             (let ((temp e1))
               (if temp
                   temp
                   (my-or e2 ...)))))))
  (let ((x #f)
        (y 7)
        (temp 8)
        (let odd?)
        (if even?))
    (my-or x
           (let temp)
           (if y)
           y)))         ⇒ 7

다음 예는 let-syntaxletrec-syntax가 어떻게 다른지 강조해요.

(let ((f (lambda (x) (+ x 1))))
  (let-syntax ((f (syntax-rules ()
                    ((f x) x)))
               (g (syntax-rules ()
                    ((g x) (f x)))))
    (list (f 1) (g 1)))) 
⇒ (1 2)

(let ((f (lambda (x) (+ x 1))))
  (letrec-syntax ((f (syntax-rules ()
                       ((f x) x)))
                  (g (syntax-rules ()
                       ((g x) (f x)))))
    (list (f 1) (g 1)))) 
⇒ (1 1)

두 표현식은 첫 번째 표현식의 let-syntax 형식이 두 번째에서는 letrec-syntax 형식이라는 점을 제외하면 동일해요. 첫 번째 표현식에서 g 안에 나타나는 flet-바인딩 변수 f를 가리키는 반면, 두 번째에서는 letrec-syntax 형식이 확립하는 바인딩을 가진 키워드 f를 가리켜요.

더 알아보기 (Learn more)

출처: Binding constructs for syntactic keywords - R6RS