在Scheme中编写myletstar宏(卫生)

时间:2018-04-11 17:38:04

标签: macros scheme racket hygiene

我正在尝试重新编写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* - > 错误:lambda:不是以下标识符:(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))

编辑:问题得到解答,通过编辑Lief Andersen代码而不使用let添加了解决方案

(define-syntax let*
  (syntax-rules ()
    ((let*2 ([x1 e1][x2 e2] ...)body ...)
     ((lambda  (x1)
        (let* ([x2 e2] ...)
           body ...))
         e1))))

3 个答案:

答案 0 :(得分:1)

R6RS,附录B给出了let*的{​​{3}}:

(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 ...)))))

答案 1 :(得分:0)

你的第二个是最接近的。可以使用define-syntax-rule编写一个hygenic setState宏。 (如果你是在方案的实现而不是球拍中写这个,你也可以组成valueonSwitch来获得同样的效果。)

isSelected

如果你真的很迂腐,你可以制作一个hygenic let*宏:

define-syntax

答案 2 :(得分:0)

谈到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每个绑定而不是模式虽然是在宏的词法范围内,并且eveything不是在每次扩展时创建的新绑定。因此,我可以安全地做到这一点:

(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一个人不会工作。