Common lisp 为什么一个符号宏会得到一个同名的环绕let绑定的类型?
SBCL中的Macroexpand all提供了以下扩展:Common lisp 为什么一个符号宏会得到一个同名的环绕let绑定的类型?,common-lisp,sbcl,Common Lisp,Sbcl,SBCL中的Macroexpand all提供了以下扩展: (SB-CLTL2:MACROEXPAND-ALL '(LAMBDA (A B) (DECLARE ((SIGNED-BYTE 4) A)) (+ A B (SYMBOL-MACROLET ((A 1) (B 2)) (+ A B))))) => (LAMBDA (A B) (DECLARE ((SIGNED-BYTE 4) A)) (+ A B (SY
(SB-CLTL2:MACROEXPAND-ALL
'(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ A
B)))))
=>
(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ (THE (SIGNED-BYTE 4) 1)
2))))
(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(DECLARE ((SIGNED-BYTE 4) B))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ (THE (SIGNED-BYTE 4) 1) (THE (SIGNED-BYTE 4) 2)))))
为什么A
扩展到(有符号字节4)1
,而不仅仅是1
我知道这来自(声明((有符号字节4)A))
,
但这是否会影响SYMBOL-MACROLET
这难道不应该是合法的吗
扩展到非(带符号字节4)
?免责声明我不知道这是否真的回答了问题。欢迎评论和编辑。
悬而未决的问题
正如Dirk在评论中所说,在Common Lisp中,这种语言被认为是(专门用于declare
form())的部分):
symbol macrolet
有一些特有的方面。[…]一种类型
由symbol macrolet定义的名称声明在
将提及该类型的表单包装在
已定义符号的扩展
据我所知,这个问题有些争议,例如,它似乎是一个公开的问题这是强制性的还是不强制性的?请阅读以下内容:
[…]必须(或可能)由宏扩展或
如果存在
是否应用于正在展开的符号宏
有四个建议,是,否,可能,以及可能。在我上面链接的文章中阅读有关它们的内容。这四项建议中的每一项都有一个理由
SBCL可以做到这一点。我认为这是实现者的选择。
为什么??嗯,是的的基本原理给出了一个理由
有一些优点(?)
例如,对编译器来说,代码的优化可能更“容易”。看看这个
无声明,无扩展中的
:
拿这个来说:
(SB-CLTL2:MACROEXPAND-ALL
'(LAMBDA (A B)
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ A B)))))
结果很简单:
(LAMBDA (A B)
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ 1 2))))
如果您将后者放在一个非常想优化的文件中,可以这样说:
(declaim (optimize (speed 3) (debug 0) (safety 0)))
; note: forced to do GENERIC-+ (cost 10)
; unable to do inline fixnum arithmetic (cost 1) because:
; The first argument is a NUMBER, not a FIXNUM.
; The result is a (VALUES NUMBER &OPTIONAL), not a (VALUES FIXNUM &REST T).
; unable to do inline fixnum arithmetic (cost 2) because:
; The first argument is a NUMBER, not a FIXNUM.
; The result is a (VALUES NUMBER &OPTIONAL), not a (VALUES FIXNUM &REST T).
; etc.
编译后,SBCL会给你一系列警告,如下所示:
(declaim (optimize (speed 3) (debug 0) (safety 0)))
; note: forced to do GENERIC-+ (cost 10)
; unable to do inline fixnum arithmetic (cost 1) because:
; The first argument is a NUMBER, not a FIXNUM.
; The result is a (VALUES NUMBER &OPTIONAL), not a (VALUES FIXNUM &REST T).
; unable to do inline fixnum arithmetic (cost 2) because:
; The first argument is a NUMBER, not a FIXNUM.
; The result is a (VALUES NUMBER &OPTIONAL), not a (VALUES FIXNUM &REST T).
; etc.
通过声明,SBCL将放在扩展中:
现在试试这个:
(SB-CLTL2:MACROEXPAND-ALL
'(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(declare ((signed-byte 4) B))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ A B)))))
这是扩展:
(SB-CLTL2:MACROEXPAND-ALL
'(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ A
B)))))
=>
(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ (THE (SIGNED-BYTE 4) 1)
2))))
(LAMBDA (A B)
(DECLARE ((SIGNED-BYTE 4) A))
(DECLARE ((SIGNED-BYTE 4) B))
(+ A B
(SYMBOL-MACROLET ((A 1) (B 2))
(+ (THE (SIGNED-BYTE 4) 1) (THE (SIGNED-BYTE 4) 2)))))
把后者放在一个文件中,把优化的宣言,编译。你猜怎么着?没有警告。SBCL不再抱怨无法对代码进行硬核心优化。它能做到。由于(有符号字节4)1)
部分
因此,也许这是一种确保类型声明也会影响macrolet表单中的变量的方法,提供类型检查,并增强编译器优化代码的能力?在Common Lisp中,
let
和符号macrolet
阴影词法绑定,以及(声明((有符号字节4)a))
是一个绑定声明,因此如果SBCL所做的是将声明传播到隐藏绑定,则这是一个错误
此示例可能会更清楚地说明这一点(这不是一个好的做法,但它可以达到以下目的):
第二个a
绑定将第一个绑定隐藏起来,因此第一个绑定在第二个绑定的范围内变得不可访问
第二个a
没有任何类型声明,并且它不应该继承以前使用相同名称的词汇绑定的任何类型声明。词法绑定的类型声明应该仅应用于该特定绑定,而不管其名称如何
因此,macroexpand all
的输出形式不应该有一个a
包装对第二个a
的访问,至少一个显然来自第一个绑定。也就是说,编译器可能足够聪明,可以看到第二个a
始终是字符串,因此它可能会将其声明为字符串
以下示例仅使用let
和symbol macrolet
执行阴影操作:
(let ((a 1))
(declare (type fixnum a))
(symbol-macrolet ((a "1"))
a))
(symbol-macrolet ((a 1))
(declare (type fixnum a))
(let ((a "1"))
a))
(symbol-macrolet ((a 1))
(declare (type fixnum a))
(symbol-macrolet ((a "1"))
a))
嗯,我能找到的最接近的东西是“允许使用与LET完全相同的声明,但有一个例外:如果特殊声明将定义为符号宏LET的符号之一命名为符号宏LET,则SYMBOL-MACROLET将发出错误信号。其中一个符号的类型声明相当于将表达式包装在该符号扩展的周围。”(from),但我并不认为这是相关的(特别是,我在CLHS中找不到这种语言).根据CLHS,我认为投票结果是
否
选项。但是,我没有看到明确的参考资料证明这一点,只是在CLHS中指向问题页面的超链接中,添加了标签:否
,作为实际决定……无论如何,这在技术上是ANSI标准中的一个“缺陷”。