为什么符号宏会获得相同名称的周围let绑定类型?

时间:2012-08-20 16:03:19

标签: common-lisp sbcl

宏扩展 - 所有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)的东西?

2 个答案:

答案 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.

通过声明,SBCL将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中,letsymbol-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始终是一个字符串,因此它可能会将其声明为字符串。

以下示例仅使用letsymbol-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))