From: Bruno H. <br...@cl...> - 2004-10-20 15:56:12
|
Hi, This harmless code (loop for readtable-case in '(:upcase :downcase :preserve :invert) do (loop for *print-case* in '(:upcase :downcase :capitalize) do (setf (readtable-case *readtable*) readtable-case))) gives a TYPE-ERROR in sbcl 0.8.15: The value NIL is not of type (MEMBER :CAPITALIZE :DOWNCASE :UPCASE). But the code contains no NIL values at all! It is clear that the outer loop should be executed exactly 4 times, and the inner loop exactly (* 4 3) times. Seen in sbcl-0.8.13 and sbcl-0.8.15. Works in cmucl-19a. Bruno |
From: Christophe R. <cs...@ca...> - 2005-07-29 11:45:09
|
Bruno Haible <br...@cl...> writes: > This harmless code > > (loop > for readtable-case in '(:upcase :downcase :preserve :invert) > do (loop > for *print-case* in '(:upcase :downcase :capitalize) > do (setf (readtable-case *readtable*) readtable-case))) This code is, formally, not harmless. From CLHS 6.1.1.4 Implementations can interleave the setting of initial values with the bindings. However, the assignment of the initial values is always calculated in the order specified by the user. A variable is thus sometimes bound to a meaningless value of the correct type, and then later in the prologue it is set to the true initial value by using setq. Unfortunately, this requirement makes loop fragments such as yours involving *PRINT-CASE* above undecidable, as it is not possible in general (though it is in certain specific cases) to construct values of the correct type. To see the problem, consider (loop for a = (break) for *print-case* in '(:upcase)) and see what happens... Cheers, Christophe |
From: Bruno H. <br...@cl...> - 2005-07-29 13:41:18
|
Christophe Rhodes wrote: > To see the problem, consider > (loop for a = (break) for *print-case* in '(:upcase)) > and see what happens... Yes, this is smaller, better showcase of the bug. * (loop for a = (break) for *print-case* in '(:upcase)) ... debugger invoked on a TYPE-ERROR in thread 6399: The value NIL is not of type (MEMBER :CAPITALIZE :DOWNCASE :UPCASE). Question 1: Is this test case invalid Common Lisp? No, there's nothing wrong in it. :upcase is an allowed value for *print-case*. > From CLHS 6.1.1.4 > > Implementations can interleave the setting of initial values with > the bindings. However, the assignment of the initial values is > always calculated in the order specified by the user. A variable is > thus sometimes bound to a meaningless value of the correct type, and > then later in the prologue it is set to the true initial value by > using setq. Question 2: Is SBCL's behaviour covered by this paragraph? No, this paragraph says that the implementation may supply "a meaningless value of the correct type" But when SBCL supplies the initial value NIL for *PRINT-CASE*, this is a meaningless value of _incorrect_ type. Bruno |
From: Christophe R. <cs...@ca...> - 2005-07-29 13:55:41
|
Bruno Haible <br...@cl...> writes: > Christophe Rhodes wrote: >> To see the problem, consider >> (loop for a = (break) for *print-case* in '(:upcase)) >> and see what happens... > > Yes, this is smaller, better showcase of the bug. > > * (loop for a = (break) for *print-case* in '(:upcase)) > ... > debugger invoked on a TYPE-ERROR in thread 6399: > The value NIL is not of type (MEMBER :CAPITALIZE :DOWNCASE :UPCASE). > > Question 1: Is this test case invalid Common Lisp? > > No, there's nothing wrong in it. :upcase is an allowed value for > *print-case*. Unfortunately, I don't consider your first sentence to follow from your second. Here is another example: (defun foop (x) (and (evenp x) (not-expressible-as-sum-of-two-primes-p x))) (deftype foo () '(satisfies foop)) (defvar *b*) (declaim (type foo *b*)) (loop for a = (break) for *b* of-type foo = (generate-non-goldbach-number)) This is qualitatively the same as your example; though you are correct that it is possible to construct a safe initial value for *print-case*, it is not so in general. >> From CLHS 6.1.1.4 >> >> Implementations can interleave the setting of initial values with >> the bindings. However, the assignment of the initial values is >> always calculated in the order specified by the user. A variable is >> thus sometimes bound to a meaningless value of the correct type, and >> then later in the prologue it is set to the true initial value by >> using setq. > > Question 2: Is SBCL's behaviour covered by this paragraph? > > No, this paragraph says that the implementation may supply > "a meaningless value of the correct type" > But when SBCL supplies the initial value NIL for *PRINT-CASE*, this is > a meaningless value of _incorrect_ type. Here, I'm more in agreement: but the problem is that the requirement in 6.1.1.4 is undecideable, or impossible to implement in general (and indeed no other implementation of LOOP of which I am aware produces conforming code for your original example either). Given the impossibility of satisfying the requirement in general, and the widespread de facto violation of the requirement to produce a meaningless value of the correct type, I am of the opinion that an error is the least bad option. Cheers, Christophe |
From: Bruno H. <br...@cl...> - 2005-07-29 20:24:54
|
Christophe Rhodes wrote: > Given the > impossibility of satisfying the requirement in general, and the > widespread de facto violation of the requirement to produce a > meaningless value of the correct type, I am of the opinion that an > error is the least bad option. Indeed there appear to be three solutions to this problem: 1) Produce a macro expansion that does not do the interleaving. Such as (loop for a = (break) for *b* of-type foo = (generate-non-goldbach-number) do (something)) ==> (let ((a (break))) (*b* (generate-non-goldbach-number))) (#:first t))) (tagbody #:begin-loop (if #:first (setq #:first nil) (setq a (break) *b* (generate-non-goldbach-number))) (if nil (go #:end-loop)) (something) (go #:begin-loop) #:end-loop)) 2) Do the interleaving, and use an incorrect initial value. 3) Signal a runtime error. Most implementations do (2), SBCL does (3). But what does ANSI CL say? The user's point of view: - To a user, by saying "Implementations _can_ ... a meaningless value of the correct type". it tells that it's the implementation's duty to provide an initial value of the correct type, if it wants to do this optimization. - Nowhere does it put the burden of providing the appropriate initial value on the user. The implementor's point of view: > the problem is that the requirement > in 6.1.1.4 is undecideable, or impossible to implement in general CLHS 6.1.1.4 doesn't force an implementation to do undecidable things in this area; it it's too hairy, it can fall back on the original non- interleaving expansion (1). Bruno Footnote: If we consider (2) and (3) invalid. it appears that in order to do correct and efficent macroexpansions of LOOP, the macro expander needs to have access to type declarations. Which it can only do in the compiler. I.e. it needs to be a MACRO which does only simple things without type analysis (namely (1)), augmented with a COMPILER-MACRO which exploits the variables' types. |