Macros 在Scheme中编写myletstar宏(卫生)

Macros 在Scheme中编写myletstar宏(卫生),macros,scheme,racket,hygiene,Macros,Scheme,Racket,Hygiene,我正在尝试重新编写let*hygiene宏,我将其作为普通宏,如果可能的话,我希望将其作为hygiene宏。我对这种宏类型没有太多经验。所以我非常感谢你的帮助。另外,我的另一个workinglet*宏表示法不工作,错误与我的宏相同 工作let* (define-macro let*1 (lambda (assgn . body) (let ((loop (gensym)) (lst (gensym))) (let loop ((lst assgn)) (if (null

我正在尝试重新编写
let*
hygiene宏,我将其作为普通宏,如果可能的话,我希望将其作为hygiene宏。我对这种宏类型没有太多经验。所以我非常感谢你的帮助。另外,我的另一个working
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)中的标识符

不工作
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)))))))
为这个让人困惑的问题道歉,我已经被这个问题困扰了一段时间了,咖啡因不再有用了

一些测试(我选择了符号名称来测试符号捕获(我知道我不必这么做)):

编辑:问题已回答,添加了解决方案,无需使用编辑代码


你的第二个是最近的。可以使用编写hygenic
let*
宏。(如果您是在scheme而不是racket的实现中编写这篇文章,您也可以编写
定义语法
语法规则
,以获得相同的效果。)

如果你觉得很迂腐,你可以做一个卫生的
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*
我不认为卫生是个问题。
中的名称让
公开,并且不引入任何其他绑定

(需要兼容性/defmacro)
(定义宏let*1)
(lambda(附属机构)
(let循环((lst assgn))
(如果(空?lst)
`(开始,@body)
`((λ(,(caar lst))
,(循环(cdr lst)))
,(地籍测量仪(()())))
(展开一次
#"(让我们看一看)
((甲1)(乙2)(丙3))
(名单a b c)))
; ==> #'((lambda(a)
5942

开始
(列表a b c)
;             3))
;          2))
;       1)
我觉得还可以。现在,您可以使用
语法规则
执行同样的操作。此方法不使用scheme语言,因此您尝试执行的
(caar lst)
假定您希望在结果扩展中使用

(展开一次#'(让*2((a1)(b2)(c3))(列表a b c)))
; ==> #'(let循环((lst’((a1)(b2)(c3)))
;(如果(null?lst)
(列表a b c)
;((lambda((caar lst))
;(循环(cdr lst)))
(地籍登记册)
;              1)))
请注意,代码实际上是如何逐字复制到结果展开中的。这是因为模式中未被分解的所有内容都假定在语法本身的词法范围内。以下是如何做到这一点:

(定义语法let*2
(语法规则()
;句柄停止条件(不再绑定)
((让*2()主体…)
(开始正文…)
处理一个扩展
((让*2((名称表达式)更多绑定…)正文…)
(let((名称expr))
(让*2(更多绑定…)
正文…)(见第页)
(展开一次
#"(让我们看一看"2")
((甲1)(乙2)(丙3))
(名单a b c)))
; ==> #'((lambda(a)
;(让*2((b2)(c3))
(列表a b c)1)
展开一次
仅执行一个级别,但通过使用宏展开器,您将看到它将继续进行,直到展开所有部分

那你什么时候需要卫生?这是在宏中引入绑定的时候。标准示例通常是
swap交换两个绑定的值:

(定义语法交换!
(语法规则()
((swap!var1 var2)
(let((tmp var1))
(设定!var1 var2)
(set!var2 tmp(())))
使用
语法规则
每一个绑定(而不是模式)都在宏的词法范围内,并且每一个绑定都是在每次扩展时创建的新绑定。因此,我可以安全地这样做:

(let((tmp 1)(另2))
(交换!tmp另一个)
(另列tmp)
; ==> (2 1)
使用旧样式时,您需要:

(定义宏交换!
(lambda(var1 var2)
let((tmp(gensym“tmp”))
`(let(,tmp,var1))
(设定!,var1,var2)
(套装,var2,tmp(()()))
这适用于上述示例,但如果我要这样做:

(let((set!1)(let 2))
(交换!设置!让)
(列表集!让我们一起看)

语法规则一个将计算为
(2 1)
,而
定义宏一个将不起作用

谢谢你这么快的回复。我很担心在hygenic宏中使用递归,我不太擅长使用省略号。Hygenic let宏非常简单。是的,您可以(而且经常应该)对Hygenic宏使用递归。尽管要确保有一个基本情况,否则它将永远无法完成扩展。;)我找不到任何解决方案,我也不确定Scheme中的省略号是如何工作的,谢谢你提供了这个网站,我肯定会觉得它非常有用。我明白了,所以你可以将任何宏重新写入到Healthy宏中(即使不需要)?我喜欢上一个示例定义宏与语法规则。我想通过使用卫生学来完成大多数宏,因为有时我会迷失在准粒子中。我想知道是否有类似于Common Lisp中的macroexpand的东西。@Dyoteotyi
语法规则有时可能非常冗长。Racket有两个以上不同的宏系统,通用的宏系统使用Racket语言,同时也很卫生。在我的回答中,您可以看到我使用了
展开一次
,它和
展开
宏展开-1
宏展开
相同。此外,DrRacket中还有宏步进器,您可以一步一步地执行每个转换。这是我用得最多的一个。
(define-syntax let*
  (syntax-rules ()
    ((let*2 ([x1 e1][x2 e2] ...)body ...)
     ((lambda  (x1)
        (let* ([x2 e2] ...)
           body ...))
         e1))))
(define-syntax-rule (let* ([x1 e1]
                           [x2 e2] ...)
                      body ...)
  (let ([x1 e1])
    (let* ([x2 e2] ...)
      body ...))
(define-syntax-rule (let ([x e] ...)
                      body ...)
  ((lambda (x ...) body ...) e ...))
(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 ...)))))