Могу ли я считать, когда макрос? Я чувствую, что не могу строго рассматривать это как макрос, так как я не использую синтаксис определения, но я не могу сказать никаких практических причин, чтобы не предпочесть эту реализацию.
Это работает как макрос, но это не совсем то же самое, что и настоящий макрос, по следующим причинам:
- Основное различие между настоящим макросом и вашим "макросом" на основе eval заключается в том, что ваш подход будет оценивать все его аргументы перед его вызовом. Это очень важное отличие. Например:
(if #f (error "oops") '())
будет оцениваться как '()
, но (when-a #f (error "oops"))
вызовет ошибку.
- Это не гигиенично. Предварительно можно было бы сделать что-то вроде
(eval '(define if "not a procedure"))
, например, и это означало бы, что этот eval потерпит неудачу; if
в выражении тела "расширения" не относится к if
на сайте определения.
- Он не расширяется во время компиляции. Это еще одна важная причина для использования макроса; компилятор расширит его, и во время выполнения не будет выполняться никаких вычислений для выполнения расширения. Сам макрос полностью испарится. Остается только расширение.
Являются ли мои макросы гигиеничными?
Только when-c
и when-d
, из-за гарантий, сделанных ir-macro-transformer
и syntax-rules
. В when-b
вам придется переименовать if
и begin
, чтобы они ссылались на версии if
и begin
на сайте определения макросов.
Пример:
(let ((if #f))
(when-b #t (print "Yeah, ok")))
== expands to ==>
(let ((if1 #f))
(if1 #t (begin1 (print "Yeah, ok"))))
Это не удастся, потому что обе версии if
(здесь аннотированные с дополнительным суффиксом 1
) ссылаются на одно и то же, поэтому мы закончим вызовом #f
в позиции оператора.
Напротив,
(let ((if #f))
(when-c #t (print "Yeah, ok")))
== expands to ==>
(let ((if1 #f))
(if2 #t (begin1 (print "Yeah, ok"))))
Который будет работать по назначению. Если вы хотите переписать when-b
, чтобы сделать его более гигиеничным, сделайте это следующим образом:
(define-syntax when-b
(er-macro-transformer
(lambda (exp rename compare)
(let ((condition (cadr exp))
(body (cddr exp))
(%if (rename 'if))
(%begin (rename 'begin)))
`(,%if ,condition (,%begin ,@body) '())))))
Обратите внимание на дополнительные идентификаторы с префиксом %
, которые относятся к исходному значению if
и begin
, поскольку они были в месте определения макроса.
Есть ли разница между when-b и when-c? Поскольку я не использую ни переименование, ни инъекцию, я думаю, что нет.
Есть. Макросы неявного переименования называются так потому, что они неявно переименовывают все идентификаторы, поступающие с сайта использования, а также каждый новый идентификатор, который вы вводите в теле. Если вы вводите какие-либо идентификаторы, это отменяет это неявное переименование, что делает их негигиенично доступными для захвата вызывающим кодом.
С другой стороны, макросы явного переименования называются так потому, что вы должны явно переименовывать любые идентификаторы, чтобы предотвратить их захват вызывающим кодом.
person
sjamaan
schedule
08.01.2018