宏扩展 - 所有SBCL都给了我以下扩展:
(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))))
为什么A
扩展为(THE (SIGNED-BYTE 4) 1)
而不只是1
?
我知道这来自(DECLARE ((SIGNED-BYTE 4) A))
,
但是这会影响SYMBOL-MACROLET
吗?
它甚至不应该有效
扩展到不是(SIGNED-BYTE 4)
的东西?
答案 0 :(得分:2)
免责声明 我不知道这是否真的能回答这个问题。欢迎提出意见和修改。
正如Dirk在评论中所说,在Common Lisp中,语言被称为(专用于declare
形式的部分(link)):
symbol-macrolet
特有的某些方面。 [..] 一种 声明定义by symbol-macrolet
的名称相当于 包裹the
表单的效果,提及该类型 扩展定义的符号。
据我所知,这个问题有点争议,例如: 这似乎是一个悬而未决的问题。 是强制还是否?请在此处阅读:
Issue SYMBOL-MACROLET-TYPE-DECLARATION Writeup
[..]必须(或可能)MACROEXPAND或者返回的值 如果有类型声明,则MACROEXPAND-1包含一个THE表单 适用于正在扩展的符号宏?
有四个提案, YES , NO , MAYBE 和可能。在我上面链接的文章中阅读它们。四个提案中的每一个都有一个基本原理。
SBCL这样做。我认为这是实现者的选择。
为什么呢?那么, YES 的基本原理给出了一个理由。
例如,编译器的代码优化可能稍微“简单”一些。检查一下。
the
:拿这个:
(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)))
你编译它,SBCL会给你一堆这样的警告:
; 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.
the
置于扩展中:现在试试这个:
(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)))))
这是扩展:
(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)))))
将后者放在一个文件中,把declaim用于优化,编译。你猜怎么着?没有警告。 SBCL不再抱怨无法对您的代码进行一些hardocore优化。它可以做到。由于(THE (SIGNED-BYTE 4) 1)
部分。
More about the the special form
所以也许这是确保你的类型声明也会影响macrolet形式的变量,提供类型检查,并强制编译器优化代码的能力的方法吗?
答案 1 :(得分:1)
在Common Lisp中,let
和symbol-macrolet
影子词法绑定,(declare ((signed-byte 4) a))
是绑定声明,所以如果SBCL正在做的是将声明传播到阴影绑定。
这个例子可能会让它更清晰(不是一个好的做法,但它可以达到目的):
(let ((a 1))
(declare (type fixnum a))
(let ((a "1"))
a))
第二个a
绑定会影响第一个,因此第一个绑定会在第二个范围内无法访问。
第二个a
没有任何类型声明,并且它不应该从以前具有相同名称的词法绑定继承任何声明。词法绑定的类型声明应该仅应用于该特定绑定,无论其名称如何。
因此,macroexpand-all
的输出形式不应该包含the
包含对第二个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))