Check if an identifier is lexically bound at the point of macro expansion?

时间:2017-12-18 05:39:57

标签: macros scheme r6rs chez-scheme

This is probably a bit of a newbie question, but I'm trying to write a macro that detects whether an identifier is lexically bound at the point of macro expansion, and changes its output accordingly. Is this possible in R6RS Scheme, and if so, how?

Why I Want to Know

I'm writing a toy Objective-C binding layer in Chez Scheme, using macros, and my initial challenge is in dealing with Objective-C selectors efficiently. The program has to query the Objective-C runtime, at runtime, for the actual SEL object corresponding to each selector name. The selector names used in the program will be known statically, during expansion, and it's easy enough to have my macros insert that query code, but I want to avoid repeat queries for the same selector name.

My first thought on this is to have some naming convention for Scheme definitions bound to SEL foreign objects. That way, I could have one (define) for each unique selector, and thus one runtime query per selector. This hinges on my macros being able to detect these bindings for any given selector, and introducing them if they do not already exist, hence my question.

This solution is still imperfect, since my macros may be expanded in inner scopes, but it was the most obvious to me. Is there a better way to "intern" expressions at expansion time?

1 个答案:

答案 0 :(得分:1)

我不完全确定这是否可以通过明确指定的行为来实现,但您可以像下面这样做:

#!r6rs
(library (bound helper)
    (export make-binding-check)
    (import (rnrs))
(define-syntax make-binding-check
  (lambda (x)
    (syntax-case x ()
      ((_ if bound?)
       #'(begin
           (define-syntax if
             (lambda (xx)
               (syntax-case xx ()
                 ((_ b then els)
                  (free-identifier=? (datum->syntax #'if (syntax->datum #'b)) #'b)
                  #'els)
                 ((_ b then els)
                  #'then))))
           (define-syntax bound?
             (lambda (xx)
               (syntax-case xx ()
                 ((_ b)
                  #'(if b #t #f))))))))))
)
(library (bound)
    (export bound? if-bound)
    (import (bound helper))
(make-binding-check if-bound bound?)
)

(import (rnrs) (bound))

(display (bound? foo)) ;; #f
(let ((foo 1))
  (display (bound? foo))) ;; #t
(newline)

这个想法是使用free-identifier=?检查给定的标识符是否绑定。 make-binding-check宏需要2个标识符,当扩展宏时,它们都应该是未绑定的。要生成此类未绑定标识符,代码包含2部分,实现和环境:第一个是(bound helper),它提供标识符比较的实现。另一个是(bound),它几​​乎不提供任何绑定环境。

使用从环境库传递的标识符和要检查的实际标识符进行比较。如果实际标识符绑定到任何内容,则它与未绑定标识符不同,因此bound?应返回#f。 (如果你定义make-binding-check并检查,它会返回#t,因为它是在(bound)库中定义的。)

<强>注意 根据您使用的实现,这可能会也可能不会起作用,我不确定这是否适用于Chez。