【问题标题】:How does symbol-macrolet handle let shadowing?符号宏如何处理让阴影?
【发布时间】:2024-01-20 00:01:01
【问题描述】:

来自CLHS

symbol-macrolet 在词法上为每个以符号命名的符号宏建立扩展函数。

...

symbol-macrolet 的使用可以被 let 遮蔽。

这允许以下代码工作(在 *b* x 内绑定到 '1'):

CT> (with-slots (x y z) *b* 
      (let ((x 10))
        (format nil "~a ~a ~a" x y z)))
"10 2 3"

我的问题是:symbol-macro 如何让知道哪些形式可以影响它?我问,因为宏不能保证 let 没有被重新定义,或者用户没有创建另一个表单来完成与 let 相同的工作。这是只寻找 cl:let 符号的特殊“愚蠢”案例吗?还是有一些更先进的技术在进行?

如果这个问题太模糊,我很乐意编辑这个问题,我很难清楚地表达这个问题。

【问题讨论】:

  • 我确实不确定您要问什么,但请记住,symbol-macroletlet 本身都是特殊运算符——积木的语言。它们不应该被重新定义或遮蔽,否则就会出现混乱。
  • 是的,我想知道 let 的特殊性是否与如何完成有关。看了 Ben 的回答后,我了解到宏可以获取有关它们所处的词汇环境的信息,这些信息非常有趣并且可能相关......我需要阅读!

标签: macros common-lisp


【解决方案1】:

参见 3.1.1.4 及周边材料。

这句话出自哪里?我不认为它是完全正确的,因为 let 不是唯一可以在词法环境中遮蔽由 macrolet 建立的名称的东西。

我认为揭示词汇环境不仅仅是一个抽象概念,还有一个实际的数据结构来体现它并没有多大害处。词法环境在编译时通过 &environment 绑定机制可供宏使用。宏可以使用它来获得进入环境的窗口,并且有一个使用环境的协议。因此,举个简单的例子,可以编写对词法环境中的声明敏感的宏,例如,如果变量声明为 fixnum,则扩展一种方式。

环境的实现由实现者决定,但它只是一堆包含名称信息的名称。因此 lambda 绑定、macrolet、标签、let* 等只是将新名称推送到该堆栈中,从而掩盖旧名称。词法声明正在添加到有关名称的信息中。

然后编译器或评估器使用环境(堆栈)来指导生成的代码或执行的行为方式。值得注意的是,这种数据结构不需要在运行时继续存在,尽管它的某些后代通常会帮助调试器。

所以回答你的问题:macrolet 不知道它的 &body 中的表单可能在做什么。

【讨论】:

  • 引用的文本来自我在问题顶部链接的超规范页面,但示例代码是我的。虽然我对 &environment 一无所知,但非常感谢,不知道我是怎么错过的,但我真的需要读一读!
【解决方案2】:

正如您所见,SYMBOL-MACROLET 是 Common Lisp 的内置功能。就像让。这些特殊运算符不能被重新定义,也不允许这样做。

Common Lisp 只有一组固定的特殊运算符,用户无法定义。只定义了这些:Common Lisp special operators

由于宏将被扩展,它们将扩展为基本原语:函数调用和特殊形式。因此编译器/解释器实现了符号宏,这个任务受限于原始形式的数量。如果用户实现了他/她自己的 LET,最终这个实现也归结为函数调用和特殊形式——宏的所有使用最终都将扩展到这些。但这些都是已知的,对于符号宏没有什么新东西。

【讨论】:

    最近更新 更多