Macros define和let w.r.t.语法规则关键字之间的差异

Macros define和let w.r.t.语法规则关键字之间的差异,macros,scheme,racket,Macros,Scheme,Racket,我是一个初学者在球拍以及Lisp和玩语法定义。我定义了一个简单的转换,如下所示: (define-syntax hello (syntax-rules (in) ((_ name in world) (format "Hello ~a in ~a" name world)) ((_ in name) (format "Hello ~a in here" name)))) 现在,当我像这两种情况一样运行它时,它工作得很好: (hello "me" in "world") ==

我是一个初学者在球拍以及Lisp和玩语法定义。我定义了一个简单的转换,如下所示:

(define-syntax hello
  (syntax-rules (in)
    ((_ name in world) (format "Hello ~a in ~a" name world))
    ((_ in name) (format "Hello ~a in here" name))))
现在,当我像这两种情况一样运行它时,它工作得很好:

(hello "me" in "world") === "Hello me in world"
以及

但是,这会导致一个错误

(let ([in "Shire"])
  (hello "Martin" in in)) === Error: hello: bad syntax in: (hello "Martin" in in)

那么,hello在let绑定中失败的原因是什么,而它在define中工作良好?另外,我在哪里可以获得关于这种差异的更具体的信息?谢谢。

这与语法文字的工作方式有关。特别是,在以下情况下,语法文字被视为匹配:

  • 文字在宏定义时没有绑定,在宏使用时也没有绑定
  • 文字在宏定义点有一个绑定,在宏使用点也有相同的绑定
  • define
    案例适合您,因为您可能将
    define
    与宏放在同一个模块中。这意味着标准2匹配:中的
    在宏定义和用法上具有相同的绑定。但是,如果您在一个模块中定义了宏(没有在
    中为定义),并且在使用宏的另一个模块中定义了,事情就不会那么顺利了。:-)


    let
    案例为中的
    创建一个新绑定。这永远不会与顶级宏定义中的
    的绑定匹配。

    原因有点微妙,“应该”也不起作用,但当
    定义
    处于顶级时,它实际上会改变
    语法规则的解释,这就是行为产生的原因

    如果您查看,您将看到每个
    文字id
    的处理方式与中相同。文档注意到,使用
    自由标识符=?
    比较文本:

    literal id
    具有相同绑定的
    id
    与语法对象相匹配,该语法对象是具有相同绑定的标识符

    这意味着文字是作为绑定匹配的,而不是作为基准匹配的。也就是说,如果在
    let
    中有
    中的
    的本地定义,它是与
    中的
    语法规则中标记为文字的单独绑定,因此它们将不匹配。此外,在单独模块中使用
    define
    引入的标识符也将不匹配

    有趣的是,如果从定义宏的模块导出
    中的
    ,然后使用
    中的
    重命名导入,则重命名的绑定仍然有效,因为Racket跟踪重命名,并认为它们是相同的绑定,即使它们不是相同的名称。这些都是正交的概念,即使有时很容易忽略这个概念的所有细微差别,但这在某种程度上是卫生固有的

    回到所讨论的例子,为什么下面的方法有效

    (define-syntax hello
      (syntax-rules (in)
        ((_ name in world) (format "Hello ~a in ~a" name world))
        ((_ in name) (format "Hello ~a in here" name))))
    
    (define in "inside")
    (hello "me" in in)
    
    嗯,定义上下文是相互递归的,因此在宏定义之后,
    中的
    定义实际上是由
    语法规则
    提取的。因此,
    hello
    宏实际上需要与
    中的
    相同的绑定,因此调用可以工作。但是,如果在与
    中的
    不同的模块中定义了
    hello
    ,则宏调用将不起作用,因为绑定不同:

    (module hello racket
      (provide hello)
      (define-syntax hello
        (syntax-rules (in)
          ((_ name in world) (format "Hello ~a in ~a" name world))
          ((_ in name) (format "Hello ~a in here" name)))))
    
    (require 'hello)
    
    (define in "inside")
    (hello "me" in in) ; => hello: bad syntax in: (hello "me" in in)
    

    因此,如果我理解正确,则由于条件1,
    let
    失败,即
    中的
    绑定在宏使用期间存在,但在定义宏时不存在。但是,我仍然不清楚
    define
    ,因为在定义宏时也不存在这种绑定。我基本上在Racket的编辑器中定义了宏,运行它,然后通过REPL中的
    define
    绑定中定义了
    。所以在这种情况下,第二个标准不应该匹配。或者我遗漏了什么?而且,当有一个全局
    定义
    时,这种情况也会发生。在函数中有一个
    (define n“something”)
    ,然后调用
    (hello“ok”in)
    也会导致相同的错误。正如Alexis的回答所解释的,模块级
    define
    影响模块中的所有内容(“相互递归”是她使用的措辞,但仍然是相同的含义),而不仅仅是后面的内容。对于函数中的内部
    define
    ,与模块级
    define
    的处理方式不同。正确,在阅读Alexis的答案后,我重新阅读了您的答案,这是有意义的。谢谢。顺便说一句,我刚刚在IRC上看到你关于球拍的问题。你一定要坚持2分钟以上。IRC主要是一种潜在的媒体;如果您只发布实际问题(不等待任何人回复“有人能帮忙吗?”请求),然后等待24小时等待回复,您将获得最佳结果。:-)(但是,正如你所知道的,莱西·兰姆达和我都能在这里回答,所以这终究是可行的。)这就是原因。谢谢
    (module hello racket
      (provide hello)
      (define-syntax hello
        (syntax-rules (in)
          ((_ name in world) (format "Hello ~a in ~a" name world))
          ((_ in name) (format "Hello ~a in here" name)))))
    
    (require 'hello)
    
    (define in "inside")
    (hello "me" in in) ; => hello: bad syntax in: (hello "me" in in)