4

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
     (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吗?

扩展到不是 a 的东西甚至不应该是有效的(SIGNED-BYTE 4)吗?

4

2 回答 2

2

免责声明我不知道这是否真的回答了这个问题。欢迎评论和编辑。

一个开放的问题

正如 Dirk 在评论中所说,在 Common Lisp 语言中说(专用于declare表单的部分(链接)):

有一些特殊的方面symbol-macrolet。[..] 定义名称的类型声明by symbol-macrolet实际上等同于将the提及该类型的表单包装在已定义符号的扩展周围。

据我所知,这个问题有些争议,例如它似乎是一个未解决的问题是强制的还是不强制的? 在这里阅读:

发布 SYMBOL-MACROLET-TYPE-DECLARATION 说明

[..] 如果存在适用于扩展符号宏的类型声明,则 MACROEXPAND 或 MACROEXPAND-1 返回的值是否必须(或可能)包含 THE 形式?

有四个提议,YESNOMAYBEPROBABLY。在我上面链接的文章中阅读它们。这四个提案中的每一个都有一个基本原理。

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.

通过声明,SBCLthe进行了扩展:

现在试试这个:

(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)))))

将后者放在一个文件中,放入声明进行优化,编译。你猜怎么着?没有警告。SBCL 不再抱怨无法对您的代码进行一些核心优化。它可以做到。因为(THE (SIGNED-BYTE 4) 1)部分。

更多关于the special form

因此,也许这是一种确保您的类型声明也会影响宏形式中的变量,提供类型检查并强制编译器优化代码的能力的方法?

于 2012-08-20T20:58:11.017 回答
1

在 Common Lispletsymbol-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))
于 2012-08-30T12:38:17.233 回答