Написание myletstar макроса (гигиена) в схеме
Я пытаюсь переписать let*
макрос гигиены, у меня он есть как обычный макрос, и я хотел бы иметь его как макрос гигиены, если это возможно. У меня не слишком много опыта с этими типами макросов. Так что я был бы очень признателен за помощь. Также мое другое представление о работе let*
макрос не работает, с той же ошибкой, что и гигиенический макрос.
За работой let*
(define-macro let*1
(lambda (assgn . body)
(let ((loop (gensym))
(lst (gensym)))
(let loop ((lst assgn))
(if (null? lst)
`(begin ,@body)
`((lambda (,(caar lst))
,(loop (cdr lst)))
,(cadar lst)))))))
Не работает гигиена let*
-> ошибка: лямбда: не идентификатор в: (caar lst)
(define-syntax let*2
(syntax-rules ()
((let*2 (set ...) body ...)
(let loop ((lst '(set ...)))
(if (null? lst)
body ...
((lambda ((caar lst))
(loop (cdr lst)))
(cadar lst) 1))))))
Не работает let*
но также с той же ошибкой, что и вторая.
(define-macro let*3
(lambda (assgn . body)
(let ((loop (gensym))
(lst (gensym)))
`(let ,loop ((,lst assgn))
(if (null? ,lst)
(begin ,@body)
((lambda ((caar ,lst))
(,loop (cdr ,lst)))
(cadar ,lst)))))))
Извиняюсь за небольшой запутанный вопрос, я застрял с этой проблемой в течение некоторого времени, и кофеин больше не полезен.
Некоторые тесты (я выбрал имена символов для проверки захвата символов (я знаю, что мне не пришлось)):
(let*1 ((body 10)
(lst (+ body 1)))
(list body lst))
(let*2 ((body 10)
(lst (+ body 1)))
(list body lst))
(let*3 ((body 10)
(lst (+ body 1)))
(list body lst))
Изменить: Ответ на вопрос, добавлено решение без использования пусть путем редактирования кода Leif Andersen
(define-syntax let*
(syntax-rules ()
((let*2 ([x1 e1][x2 e2] ...)body ...)
((lambda (x1)
(let* ([x2 e2] ...)
body ...))
e1))))
3 ответа
Ваш второй самый близкий. Гигиенический let*
макрос может быть записан с define-syntax-rule
, (Если вы пишете это в реализации схемы, а не ракетки, вы также можете просто составить define-syntax
а также syntax-rule
для того же эффекта.)
(define-syntax-rule (let* ([x1 e1]
[x2 e2] ...)
body ...)
(let ([x1 e1])
(let* ([x2 e2] ...)
body ...))
И если вы чувствуете себя по-настоящему педантичным, вы можете сделать гигиенический let
макрос:
(define-syntax-rule (let ([x e] ...)
body ...)
((lambda (x ...) body ...) e ...))
R6RS, Приложение B дает следующее определение let*
:
(define-syntax let*
(syntax-rules ()
((let* () body1 body2 ...)
(let () body1 body2 ...))
((let* ((name1 expr1) (name2 expr2) ...)
body1 body2 ...)
(let ((name1 expr1))
(let* ((name2 expr2) ...)
body1 body2 ...)))))
Когда дело доходит до let*
Я не думаю, что гигиена является проблемой. Имена в let
Вы хотите выставить, и вы не вводите никаких других привязок.
(require compatibility/defmacro)
(define-macro let*1
(lambda (assgn . body)
(let loop ((lst assgn))
(if (null? lst)
`(begin ,@body)
`((lambda (,(caar lst))
,(loop (cdr lst)))
,(cadar lst))))))
(expand-once
#'(let*1
((a 1) (b 2) (c 3))
(list a b c)))
; ==> #'((lambda (a)
; ((lambda (b)
; ((lambda (c)
; (begin
; (list a b c)))
; 3))
; 2))
; 1)
Кажется, хорошо для меня. Теперь вы можете сделать то же самое, используя syntax-rules
, Этот метод не использует язык схем и поэтому вы пытаетесь сделать (caar lst)
Предполагается, что вы хотите, чтобы в результате расширения.
(expand-once #'(let*2 ((a 1) (b 2) (c 3)) (list a b c)))
; ==> #'(let loop ((lst '((a 1) (b 2) (c 3))))
; (if (null? lst)
; (list a b c)
; ((lambda ((caar lst))
; (loop (cdr lst)))
; (cadar lst)
; 1)))
Обратите внимание, как ваш код фактически копируется дословно в полученное расширение. Это потому, что все, что не деструктурировано в шаблоне, предполагается в лексической области самого синтаксиса. Вот как это сделать:
(define-syntax let*2
(syntax-rules ()
;; handle stop condition (no more bindings)
((let*2 () body ...)
(begin body ...))
;; handle one expansion
((let*2 ((name expr) more-bindings ...) body ...)
(let ((name expr))
(let*2 (more-bindings ...)
body ...)))))
(expand-once
#'(let*2
((a 1) (b 2) (c 3))
(list a b c)))
; ==> #'((lambda (a)
; (let*2 ((b 2) (c 3))
; (list a b c))) 1)
expand-once
только один уровень, но с помощью макроэкспандера вы увидите, что он продолжается до тех пор, пока все части не будут расширены.
Итак, когда вам нужна гигиена? Это когда вы вводите привязки в ваш макрос. Стандартный пример обычно swap!
это меняет значения двух привязок:
(define-syntax swap!
(syntax-rules ()
((swap! var1 var2)
(let ((tmp var1))
(set! var1 var2)
(set! var2 tmp)))))
С syntax-rules
хотя каждая привязка, а не шаблон, должна находиться в лексической области действия макроса, и все это не является новой привязкой, которая создается новой при каждом расширении. Таким образом, я могу безопасно сделать это:
(let ((tmp 1) (another 2))
(swap! tmp another)
(list tmp another))
; ==> (2 1)
Со старым стилем вам нужно это:
(define-macro swap!
(lambda (var1 var2)
(let ((tmp (gensym "tmp")))
`(let ((,tmp ,var1))
(set! ,var1 ,var2)
(set! ,var2 ,tmp)))))
Это работает для примера выше, но если бы я должен был сделать:
(let ((set! 1) (let 2))
(swap! set! let)
(list set! let))
Синтаксические правила, которые нужно оценить (2 1)
в то время как define-macro
никто не будет работать.