В качестве аналога Ответ Sorawee Porncharoenwase (который является правильным ответом) Я думаю, стоит задуматься над тем, почему ваш макрос test
проблематичен c в CL и почему макросы, которые делают подобные все просто глючит.
Учитывая ваш макрос test
, представьте, что какой-то пользователь смотрит на этот код:
(let ((a 1) (b 2))
(test (+ a b)))
Ну, я не знаю о вас, но чего бы я ожидал случиться так, что a
и b
внутри test
- это a
и b
, которые я только что связал. Но это совсем не так, конечно.
Что ж, возможно, документация для test
очень подробно описывает, что она связывает две переменные, и это то, чего я должен ожидать. И, конечно, есть макросы, которые делают именно это, и где это нормально:
(defmacro awhen (test &body forms)
`(let ((it ,test))
(when ,it ,@forms)))
А теперь:
(awhen (find-exploder thing)
(explode it))
И это все хорошо, потому что документация для awhen
скажет, что оно связывает it
с результатом теста в своем теле.
Но теперь рассмотрим этот or
макрос, украденный из другого ответа:
(defmacro vel (a b)
`(let ((a-val ,a))
(if a-val a-val ,b)))
Это это катастрофа. Он «работает», за исключением того, что он вообще не работает:
> (let ((a-val 3))
(vel nil a-val))
nil
Теперь это не просто удивительно в том, как ваш макрос test
: он неправильный.
Вместо этого вы нужно написать vel
как это в CL:
(defmacro vel (a b)
(let ((a-val-name (make-symbol "A-VAL")))
`(let ((,a-val-name ,a))
(if ,a-val-name ,a-val-name ,b))))
(Вы, конечно, можете использовать gensym
вместо make-symbol
, и я думаю, что большинство людей.)
И теперь
> (let ((a-val 3))
(vel nil a-val))
3
, как и следовало ожидать.
Это все потому, что система макросов CL unhygeni c - она гарантирует, что такие вещи, как имена не кла sh. В CL вы должны go немного уйти от написания макросов, которые во многих случаях верны. Макросистема Racket, с другой стороны, имеет вид hygeni c: по умолчанию она гарантирует, что имена (и другие вещи) не будут sh. В Racket (и Scheme) вы должны go из своего способа писать макросы, которые либо некорректны, либо делают что-то немного неожиданное, например, вводят привязки, видимые из кода, использующего макросы.
Обратите внимание, что я Я не выражаю предпочтения ни одному из подходов к макросам: я провел большую часть своей жизни за написанием CL, и я очень доволен его макросистемой. Совсем недавно я написал больше Racket, и я также доволен его системой макросов, хотя мне труднее ее понять.
Наконец, вот вариант вашего макроса, который менее удивителен в использовании (почти весь шум в этом коде - проверка работоспособности, которую syntax-parse
поддерживает в виде двух #:fail-when
предложений):
(define-syntax (with-char-codes stx)
(syntax-parse stx
[(_ (v:id ...) form ...)
#:fail-when (check-duplicate-identifier (syntax->list #'(v ...)))
"duplicate name"
#:fail-when (for/or ([name (syntax->list #'(v ...))])
(and (> (string-length (symbol->string
(syntax->datum name)))
1)
name))
"name too long"
#'(let ([v (char->integer (string-ref (symbol->string 'v) 0))] ...)
form ...)]))
А теперь
> (with-char-codes (a b)
(+ a b))
195