ruffwork@orstcs.CS.ORST.EDU (Ritchey Ruff) (12/17/87)
I think I see the basic point of dissent in this discussion (but when I posted the original I never thought it would come to this ;-). The main difference in opinion seems to be what we "feel" the definition of "A CORRECT PROGRAM" is, right? I tend to come from a software engineering viewpoint, so I'll make my bast stab at it - A correct program is one that - - has a specification of correct behavour for both correct *and* *incorrect* input; - will give the correct output for *ANY* possible input (using above specifiaction for validation). This means - (1) common lisp IS correct (it is following its definition ;-), but (2) it makes it VERY hard for programmers to write "portable" correct code because Steele et.al. underspecified the definition of the language. You are FORCED to either use a subset of the whole language or validate it on every CL implementation (and even version) you will run it on. This get right back to my original conclusion - *IF* you follow my definition of "correct", *AND* you want to have "portable" code, YOU CAN'T USE STATEMENTS THAT EFFECT THE SEMANTICS OF THE PROGRAM BUT THAT CAN BE IGNORED because then (by definition) you will not have a program that you can be sure is "correct" when running under CL implementations that you have not validated it on. *This* was my original gripe! Comments? (Just a sec while I don my welding goggles and gloves and my aspestos suit ;-) --ritchey ruff ruffwork@cs.orst.edu -or- "Lisp is the best way to ruffwork%cs.orst.edu@relay.cs.net -or- abuse a computer." -anon. { hp-pcd | tektronix }!orstcs!ruffwork
andy@rocky.STANFORD.EDU (Andy Freeman) (12/17/87)
In article <1547@orstcs.CS.ORST.EDU> ruffwork@CS.ORST.EDU (Ritchey Ruff) writes: >I tend to come from a software engineering viewpoint, so I'll make my >bast stab at it - > A correct program is one that - > - has a specification of correct behavour for both > correct *and* *incorrect* input; > - will give the correct output for *ANY* possible input > (using above specifiaction for validation). >This means - > (1) common lisp IS correct (it is following its definition ;-), but > (2) it makes it VERY hard for programmers to write "portable" > correct code because Steele et.al. underspecified the > definition of the language. You are FORCED to either use > a subset of the whole language or validate it on every > CL implementation (and even version) you will run it on. (2) is false. Ruff's program was incorrect by his definition. Others have covered Ruff's misunderstanding of what common lisp declarations are. His (unstated) specification required code that he didn't write; I'll sketch an appropriate PORTABLE defintion. The original program was something like (defun silly (a b) (declare (integer a b)) (loop i from a to b do (print i))) Silly does not handle illegal input - its definition promises that its input is well-formed. Safe-silly handles illegal input. (defun safe-silly (a b) <Put code here that tests whether the input is legal and takes appropriate action if it isn't. Note that competent CL programmers know how to return a value from safe-silly without executing the remainder of this function.> (let ((a a) (b b)) (declare (integer a b)) (loop i from a to b do (print i)))) [Insert some sarcastic comments here.] This is the same sort of thing one must do in every language. For example, one must test input to pascal programs. [I don't feel like explaining this so insert more sarcasm here.] -andy -- Andy Freeman UUCP: {arpa gateways, decwrl, sun, hplabs, rutgers}!sushi.stanford.edu!andy ARPA: andy@sushi.stanford.edu (415) 329-1718/723-3088 home/cubicle
gudeman@arizona.edu (David Gudeman) (12/18/87)
In article <1547@orstcs.CS.ORST.EDU> ruffwork@orstcs.CS.ORST.EDU (Ritchey Ruff) writes: >I think I see the basic point of dissent in this discussion... > >The main difference in opinion seems to be what we "feel" the definition >of "A CORRECT PROGRAM" is, right? No, I don't think you get it. You are trying to use type declarations to make the run-time system verify the types of variables. You are using them incorrectly. Type declarations in Common Lisp have no purpose other than to give the compiler information that helps it produce efficient code. That's why the compiler is free to ignore the declarations. Some implementations may use the declarations to help the programmer find bugs, but this is not part of the language. So if you depend on type declarations to check the types of your variables at run time, you are using a non-standard, non-portable feature of a specific implementation.
barmar@think.COM (Barry Margolin) (12/19/87)
In article <859@rocky.STANFORD.EDU> andy@rocky.UUCP (Andy Freeman) writes: >The original program was something like > >(defun silly (a b) > (declare (integer a b)) > (loop i from a to b do (print i))) Actually, I think he said that the program was something like (defun silly (a b) (sloop for i from a to b do (print i))) and the SLOOP macro expands into code that contains somwething like (declare (integer i a b)), but it doesn't check whether A and B actually are integers at runtime. Something is incorrect here, the question is whether it is SILLY or SLOOP, and it depends on SLOOP's documentation. If the documentation for the FROM/TO keyword says "the arguments must evaluate to integers" then it becomes SILLY's responsibility to check its arguments. If the documentation doesn't say this then SLOOP should include type-checking code in its expansion or should expand into code that doesn't assume a particular type. >Silly does not handle illegal input - its definition promises that >its input is well-formed. Safe-silly handles illegal input. [omitted] >This is the same sort of thing one must do in every language. >For example, one must test input to pascal programs. [I don't >feel like explaining this so insert more sarcasm here.] Well, yes and no. In traditional Algol-derived languages the compiler will not let you pass incorrect data types. The type of all expressions can be determined at compile time, the types of arguments expected by subroutines are declared, so argument type checking can be done. You can't execute a Pascal program in which a floating point value is passed to a routine expecting an integer. If you have argument constraints that go beyond what the language's type grammar allows you to specify (for example, if a routine requires that its argument be an even integer) then you are correct that the routine must do its own input checking. However, since the type specification doesn't let you say this, it is not a case of a redundant check. --- Barry Margolin Thinking Machines Corp. barmar@think.com seismo!think!barmar
ruffwork@orstcs.CS.ORST.EDU (Ritchey Ruff) (12/19/87)
First I want to clear up that the program segment was more like - (defun foo(n) (declare (integer n)) (integer-only-function (the integer n))) Silly me. I saw that "the" assures that its value is of the type stated, but failed to note that it is simply a "declaration" for unnamed forms. In this vain I'm wrong (20 lashes with a gc'ed cons cell). I still have a gripe but will let this dead horses die... sigh...shows what happens what an Interlisp hacker tries to move to Common Lisp ( 8-0 "oh, no!" ;-). --ritchey ruff
diamant@hpfclp.HP.COM (John Diamant) (12/19/87)
> (defun silly (a b) > (declare (integer a b)) > (loop i from a to b do (print i))) > > Silly does not handle illegal input - its definition promises that > its input is well-formed. Safe-silly handles illegal input. I agree with your example about safe-silly versus silly, but I'd like to point out that safe-silly needn't be as ugly as you wrote in your example. Below your example, see a complete, correct implementation of safe-silly that doesn't require that bizarre (though quite correct) let. Note, that I am not attempting to criticize your understanding of Common Lisp -- you clearly understand it. I only wish to point out the the safe program can be very clean (more so than in your example). > > (defun safe-silly (a b) > <Put code here that tests whether the input is legal and takes > appropriate action if it isn't. Note that competent CL programmers > know how to return a value from safe-silly without executing the > remainder of this function.> > (let ((a a) (b b)) > (declare (integer a b)) > (loop i from a to b do (print i)))) (defun safe-silly (a b) (check-type a integer) (check-type b integer) (loop i from (the integer a) to (the integer b) do (print i))) Note that check-type is part of CL and provides an optional string argument to provide your own error message, and that with the use of the special form "the," no extra storage is needed. Clearly, in this example, the efficiency isn't that significant, but in heavy calculations, it may well make a big difference. In a much larger main body, the use of the let as above may well be worth it (if a and b appear several times). John Diamant UUCP: {hplabs,hpfcla}!hpfclp!diamant Hewlett Packard Co. ARPA Internet: diamant%hpfclp@hplabs.HP.COM Fort Collins, CO
shebs%defun.utah.edu.uucp@utah-cs.UUCP (Stanley T. Shebs) (01/21/88)
In article <1547@orstcs.CS.ORST.EDU> ruffwork@CS.ORST.EDU (Ritchey Ruff) writes: > A correct program is one that - > - has a specification of correct behavour for both > correct *and* *incorrect* input; > - will give the correct output for *ANY* possible input > (using above specifiaction for validation). > >This means - > (1) common lisp IS correct (it is following its definition ;-), but > (2) it makes it VERY hard for programmers to write "portable" > correct code because Steele et.al. underspecified the > definition of the language. You are FORCED to either use > a subset of the whole language or validate it on every > CL implementation (and even version) you will run it on. > >This get right back to my original conclusion - *IF* you follow >my definition of "correct", *AND* you want to have "portable" code, >YOU CAN'T USE STATEMENTS THAT EFFECT THE SEMANTICS OF THE PROGRAM >BUT THAT CAN BE IGNORED because then (by definition) you will not have >a program that you can be sure is "correct" when running under CL >implementations that you have not validated it on. *This* was my >original gripe! In other words, you want a language definition that requires all implementations to detect mistakes in the program. Presumably you're going to be generous and not require that implementations detect infinite loops that are due, say, to misuse of counters... Even given that, there are other situations, such as memory exhaustion, that a system is unlikely to deal with gracefully. Getting back to type declarations, it's surely unreasonable to expect a Common Lisp system to take an expression like (the fixnum (+ (the fixnum x) (the fixnum y)) and analyze all the possible values for x and y to make sure that their sum doesn't exceed the range of fixnums. The only truly safe choice for the compiler is to not opencode the addition, but of course that defeats the whole purpose of declarations. Instead, the CL spec says that this sort of thing "is an error", and implementations are free to do anything up to and including the erasure of your home directory :-) if the program causes such an error. The alternative is Lisp system performance that you would not like at all... Lower-level languages like Pascal can be more portable because they are more restrictive and can therefore be statically checked better. I'd say that although you do have something to gripe about, it comes with the territory, and you should plan to acquire lots of CL implementations if you want to write portable code... 1/2 :-)