Why does the character macro get the type of the surrounding let binding the same name?

Macroexpand-all in SBCL gives me the following extension:

(SB-CLTL2:MACROEXPAND-ALL '(LAMBDA (AB) (DECLARE ((SIGNED-BYTE 4) A)) (+ AB (SYMBOL-MACROLET ((A 1) (B 2)) (+ A B))))) => (LAMBDA (AB) (DECLARE ((SIGNED-BYTE 4) A)) (+ AB (SYMBOL-MACROLET ((A 1) (B 2)) (+ (THE (SIGNED-BYTE 4) 1) 2)))) 

Why (THE (SIGNED-BYTE 4) 1) A expand to (THE (SIGNED-BYTE 4) 1) , and not just 1 ?

I understand that this comes from (DECLARE ((SIGNED-BYTE 4) A)) , but should this affect SYMBOL-MACROLET at all?

Shouldn't it even expand to what is not (SIGNED-BYTE 4) ?

+4
source share
2 answers

Disclaimer I do not know if this is really the answer to the question. Comments and changes are welcome.

Open problem

As Dirk said in a comment, in Common Lisp, the Language says that (section on declare form ( link )):

There are certain aspects specific to symbol-macrolet . [..] type declaration of a certain name by symbol-macrolet equivalent to an effect for wrapping the form in which it is indicated that the type around is an extension of a certain symbol.

As far as I can tell, the problem is somewhat controversial, for example. This is an open question . Mandatory or not? Read here:

SYMBOL-MACROLET-TYPE-DECLARATION Recording Problem

[..] should (or can) return the value MACROEXPAND or MACROEXPAND-1 includes the form if there are type declarations that apply to the macro character extension?

There are four sentences: YES , NO , MAYBE and PROBABLY . Read about them in the article that was given above. Each of the four sentences has a rationale.

SBCL does this. I think this is the choice of developers.

Why? Well, the rationale for YES gives cause.


There are some advantages (?)

For example, code optimization may be somewhat β€œeasier” for the compiler. Check this.

No ads, no the in the extension:

Take this:

 (SB-CLTL2:MACROEXPAND-ALL '(LAMBDA (AB) (+ AB (SYMBOL-MACROLET ((A 1) (B 2)) (+ AB))))) 

the result is simple:

 (LAMBDA (AB) (+ AB (SYMBOL-MACROLET ((A 1) (B 2)) (+ 1 2)))) 

if you put the latter in the file you want to optimize, say something like this:

 (declaim (optimize (speed 3) (debug 0) (safety 0))) 

and you compile it, SBCL will give you a bunch of warnings like this:

 ; 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. 

With an SBCL declaration, put the in the extension:

Now try the following:

 (SB-CLTL2:MACROEXPAND-ALL '(LAMBDA (AB) (DECLARE ((SIGNED-BYTE 4) A)) (declare ((signed-byte 4) B)) (+ AB (SYMBOL-MACROLET ((A 1) (B 2)) (+ AB))))) 

this decomposition:

 (LAMBDA (AB) (DECLARE ((SIGNED-BYTE 4) A)) (DECLARE ((SIGNED-BYTE 4) B)) (+ AB (SYMBOL-MACROLET ((A 1) (B 2)) (+ (THE (SIGNED-BYTE 4) 1) (THE (SIGNED-BYTE 4) 2))))) 

Put the latter in the file, place the declaration for optimization, compile. Guess what? No. SBCL no longer complains that it will not be able to perform some stiffness optimization for your code. He can do it. Due to part (THE (SIGNED-BYTE 4) 1) .

Read more about the special form

So, maybe this is a way to ensure that your type declaration will affect the variables in the form of a macro, also check the type checking and make it possible for the compiler to optimize the code?

+2
source

In common Lisp, let and symbol-macrolet shadow lexical bindings, and (declare ((signed-byte 4) a)) is a linked declaration, so this is a mistake if what SBCL does is propagate the declaration to the binding binding .

This example can make it more understandable (not a good practice, but it serves the purpose):

 (let ((a 1)) (declare (type fixnum a)) (let ((a "1")) a)) 

The second binding a obscures the first, so the first becomes inaccessible within the scope of the second.

The second a has no type declaration, and it should not inherit any of the previous lexical bindings with the same name. Type statements for lexical bindings should only apply to this particular binding, regardless of its name.

Thus, the output form macroexpand-all should not have the , restricting access to the second a , at least to the first, which clearly arises from the first binding. That is, the compiler can be smart enough to see that the second a always a string, so it can declare it as a string.

In the following examples, just do the shading with let and 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)) 
+1
source

All Articles