R6RS Scheme: 구문 키워드를 위한 바인딩 형식
R6RS Scheme: 구문 키워드를 위한 바인딩 형식
구문 키워드들을 바인딩하는 let-syntax와 letrec-syntax 형식을 설명해요. 키워드 바인딩의 문법, 의미, 그리고 두 형식 사이의 차이를 예시로 다뤄요.
출처: 문서
본문
let-syntax와 letrec-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-syntax와 letrec-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 안에 나타나는 f는 let-바인딩 변수 f를 가리키는 반면, 두 번째에서는 letrec-syntax 형식이 확립하는 바인딩을 가진 키워드 f를 가리켜요.