ruffwork@orstcs.CS.ORST.EDU (Ritchey Ruff) (12/08/87)
Would you use a language that can arbitrarily ignore some of your code ??? Especially if different implementations ignored different statements in the same code ??? Even if it didn't TELL you what it was ignoring when ??? I have a bone to pick with Steele about something he left out of the Common Lisp definition. The above is *EXACTLY* what Common Lisp *DOES* !!! In the sections about the strong typing, "Common Lisp:The Language" says the compiler or interpreter can ignore many declarations. It should also state that there be a standard way to find out WHAT the compiler/ interpreter is ignoring (or using). Something like a compiler flag (":declares-ignored t/nil") or a global flag (*IGNORED-WARNINGS*) to force common lisp to show what it is ignoring. Why, you ask??? First, principle (I kind of like that ;-): when you put in strong typing statements (like "(the integer foo)") do you REALLY want them ignored in different ways, at different times, by different Common Lisps - and not even know which is ignoring what when ??? Second, I've just spent weeks tracking bugs caused by compilers/interpreters ignoring different parts of my declarations. Simply because an interpreter/compiler can IGNORE strong typing (like "(the integer foo)"), optimizer statements (like safety=3), and declarations (like "(declare (integer foo))") I found that code that ran ok on one version of Common Lisp would not even compile under another, run but go into a break on another, and run to completion but give wrong results on another !!!! For example - lots of people use Bill Shelters' excellent SLOOP looping macro package (thanks for all that work you put into an excellent package, Bill!). Its great, but because it tries to optimize (by default it expands with declarations that give type info on looping vars, etc.) it turns out to be non-portable. Here is a totally non-portable piece of code - (DEFUN TST (N M) (SLOOP FOR I FROM N TO M COLLECT I)) This is quite simple, right? When it expands N, M, and I get declared of type integer, and the iteration var gets checked by the "THE" statement each time it's incremented to see that it remains of type integer. Below are results from several different Common Lisps (all this was done with safety=3) --- ---------------------------------------- FranzExtendedCommonLisp> (tst 1 5) (1 2 3 4 5) FranzExtendedCommonLisp> (tst 1.0 5.0) Continuable Error: Object 2.0 is not of type FIXNUM. If continued with :continue, Prompt for a new object. [1c] <cl> ^D FranzExtendedCommonLisp> (compile 'tst) TST FranzExtendedCommonLisp> (tst 1.0 5.0) (1.0 2.0 3.0 4.0 5.0) FranzExtendedCommonLisp> (tst 1 5.0) (1 2 3 4 5) ---------------------------------------- KyotoCommonLisp> (tst 1 5) (1 2 3 4 5) KyotoCommonLisp> (tst 1.0 5.0) Error: 2.0 is not of type FIXNUM. Error signaled by THE. Broken at THE. Type :H for Help. KyotoCommonLisp>> :q KyotoCommonLisp> (compile 'tst) End Pass1. End Pass2. TST KyotoCommonLisp> (tst 1.0 5.0) (0) KyotoCommonLisp> (tst 1 5.0) NIL ---------------------------------------- AllegroCommonLisp> (tst 1 5) (1 2 3 4 5) AllegroCommonLisp> (tst 1.0 5.0) (1.0 2.0 3.0 4.0 5.0) AllegroCommonLisp> (compile 'tst) TST AllegroCommonLisp> (tst 1.0 5.0) (1.0 2.0 3.0 4.0 5.0) AllegroCommonLisp> (tst 1 5.0) (1 2 3 4 5) ---------------------------------------- So we have 3 different "Common Lisps" (and the quotes are intentional) that give radically different results for the SAME code !!! EVEN the interpreter (Help me, Spock ;-) !!! If the compiler and interpreter gave warnings when they ignored code the reason for the bugs that this type of behavior can cause would be so much easier to track down. When you have your code debugged and are looking for raw speed, a global flag could be set to stop displaying warnings of this type. MORAL OF THE STORY --- IF YOU WANT TRULY PORTABLE COMMON LISP CODE THAT WORKS THE SAME INTERPRETED AS COMPILED, *DO* *NOT* PUT STRONG TYPING OR OPTIMIZER STATEMENTS ANYWHERE IN YOUR CODE !!! IF *ANYTHING* *CAN* IGNORE A STATEMENT, *NEVER* USE THAT STATEMENT !!! I've gone on too long, but I think I've made my point. Thanks for the bandwidth, --Ritchey Ruff ruffwork@cs.orst.edu -or- "I haven't lost my mind, ruffwork%oregon-state@relay-cs-net -or- its' backed up on tape somewhere..." { hp-pcd | tektronix }!orstcs!ruffwork
quiroz@cs.rochester.edu (Cesar Quiroz) (12/08/87)
Expires: Sender: Followup-To: (Context: Ritchey Buff complains about the freedom given to the implementors to ignore certain elements of the language (declarations) without any warning to the user.) First of all, I agree that each implementation should tell you which declarations are ignored and how the used ones affect your code. (I know of at least one implementation where declarations may slow down correct code.) Indeed, all the implementations I know of at least try, so I don't see why the language specification is in fault. You need the info to decide on efficiency matters, but the info is often provided anyway. What prompts me to follow-up is the conclusion of the article. Notice carefully that you cannot demand that any implementation tag arbitrarily *incorrect* code. You can, however, compare the quality of the attempt: If an implementation catches more errors than other, the first one is probably the one you desire most. So one cannot build an argument around *incorrect* code. (Your subject line should have been `Incorrect Common Lisp code lacks portability'. We suspected that much long ago.) Your example was just wrong, the fact that one implementation missed it is unfortunate and, at most, reflects on that implementation. And without the declarations, none of the implementations you tested would have given you any error message. The conclusion should be the opposite: if you can, provide more declarations as you improve your code. High quality implementations may use that information to help you debug, lesser ones should still do something useful when your code is correct. What you propose is that, because some implementations cannot catch some errors, we should avoid discovering those bugs in all the implementations. To sum: Declarations can affect (perhaps negatively!) the efficiency of correct code, but never make a correct program incorrect. Declarations do not make incorrect code correct. The behavior of incorrect code (other than some error reporting when it is explicitly guaranteed that `an error is signalled') is, of course, undefined and well up to the implementation. And decent implementations document the decisions left up to them. -- Cesar Augusto Quiroz Gonzalez Department of Computer Science ...allegra!rochester!quiroz University of Rochester or Rochester, NY 14627 quiroz@cs.rochester.edu
gz@spt.entity.com (Gail Zacharias) (12/10/87)
Common Lisp does provide a number of ways to portably request type checking at run time, such as CHECK-TYPE and ASSERT, etc. But it doesn't specify the behavior of incorrect programs (such as programs in which declarations are false). Basically, the debugging environment is up to the implementation. I don't think that's a major shortcoming of the Common Lisp standard. The important thing is that once your program is debugged, it should run in any Common Lisp implementation. (Btw, if by Allegro Common Lisp you meant the Macintosh one, i.e. Coral Common Lisp, then here's a hint: turn off *compile-definitions*! The CCL evaluator does indeed signal an error on incorrect THE forms. The reason you didn't get an error in your example is that TST was running compiled.) ------- gz%entity.com@eddie.mit.edu gz@entity.com {backbone}!spt!gz
preece@ccvaxa.UUCP (12/12/87)
ruffwork@orstcs.CS.ORST.EDU: > Would you use a language that can arbitrarily ignore some of your code > ??? Especially if different implementations ignored different > statements in the same code ??? Even if it didn't TELL you what it was > ignoring when ??? ---------- Well, C can ignore register declarations. Declarations in CL are similarly supposed to be YOUR promise that the named objects will be of a certain type, ALLOWING the system to make that assumption if it is convenient for it to do so. The behavior of CL under many kinds of errors is not completely required. Why does it offend you so much that when you lie to different CLs (by providing non-integer arguments) different things happen? If you aren't willing to promise not to lie, you shouldn't use declarations. -- scott preece gould/csd - urbana uucp: ihnp4!uiucdcs!ccvaxa!preece arpa: preece@Gould.com
ross@ulowell.cs.ulowell.edu (Ross Miller) (12/15/87)
In article <233@spt.entity.com> gz@spt.entity.com (Gail Zacharias) writes: >Common Lisp does provide a number of ways to portably request type checking >at run time, such as CHECK-TYPE and ASSERT, etc. But it doesn't specify the >behavior of incorrect programs (such as programs in which declarations are >false). Basically, the debugging environment is up to the implementation. I >don't think that's a major shortcoming of the Common Lisp standard. The >important thing is that once your program is debugged, it should run in any >Common Lisp implementation. Wrong. Crreeeeaaaak, sound of Common lisp tomb being opened yet again, I have read and read, and reread that section looking for a way out. An implementation is free to ignore type checking and not return any errors, but it must except the code that declares type checking. That is not a quote, but it is the jist. Here is a case where a program could be perfectly debugged and still fail. What if we had an implemenation of GKS for lisp. This is being worked on by X3H3.4 of ANSI. The lisp is actually an interface to another language that the GKS is really written in. The GKS is perfect passes all the test suites etc. But now an application programmer sits down to actually use this beast. By accident he sends a float to polyline when an integer was expected. Lisp then passes the 32bit word down to say C, machine size may vary, and C blithly trys to write out a few million polylines, or some other random number or worse a negative array reference, and the OS informs us of an array overflow. Please keep in mind the actual error could vary greatly depending on what the final GKS spec looks like and what the implentation actually does but you get the idea. If the GKS is actually implemented in lisp, then lisp will return an error eventually from such garbage being passed to it, and again the applications programmer is going to have to intuitivly figure out what is wrong. Worse, nothing is wrong the GKS is perfect only his program is wrong but he will get errors unrelated to what is wrong because data with garbage types will be being passed around. In fact the resolution of the committee was garbage in garbage out. An implentation is free to check the types manually upon invokation of the function but is not required to do so. This was resolved so that code could continue to be portable across GKS applications in different lisps. On another subject have I missed the argument on why arrays are not always passed by value? Ross -- csnet: ross@ulowell.cs.ulowell.edu uucp: ross@ulowell.cs.ulowell.edu || ...harvard!ulowell!ross Trust the computer. The computer is your friend.
quiroz@cs.rochester.edu (Cesar Quiroz) (12/15/87)
In article <2126@ulowell.cs.ulowell.edu>, Ross Miller offers to show
an example of a `debugged' program that stills fails. I emphasized
`debugged', because that wasn't the issue. Here the problem is
whether the program is correct or not, not whether it has been
debugged, whatever that means. Miller's program was in error, so
his objection is moot. (I hope I am not quoting his posting out of
context due to my heavy editing):
: ... Here is a case where a program could be perfectly
:debugged and still fail.
: What if we had an implemenation of GKS for lisp. ...
:... The GKS is perfect passes all the test suites etc. ...
:By accident he sends a float to polyline when an integer
:was expected...
The scenario involves at least this incorrection. Then, the program
is not correct, never mind how energetically debugged. Never mind
CL does not specify how to pass things to a C program, etc. These
latter questions are of the highest practical importance, see below,
but shouldn't cloud our judgement as to the fact that the program
was in error and whatever happens was deserved.)
: If the GKS is actually implemented in lisp, then lisp will return
:an error eventually from such garbage being passed to it, and again the
:applications programmer is going to have to intuitivly figure out what
:is wrong. Worse, nothing is wrong the GKS is perfect only his program is
:wrong but he will get errors unrelated to what is wrong because data with
:garbage types will be being passed around.
I feel reasonable sympathy for such programmer. It is still true
that `only his program is wrong'. And that is all that is needed to
render the objection moot.
On the issue of interfacing with other languages, I think it is
clear there is no way to legislate a solution. Good ideas should be
tried, in the hopes that almost de-facto standards for specific
combinations of languages and architectures eventually emerge. If
Lispers want to carve a nice niche for their favorite language in
the numeric community, such interfaces will be needed. (But CL
already has a better defined arithmetic than many other languages,
just improving the Lisp compilers might be a better idea. Burn
those dusty decks :-)
--
Cesar Augusto Quiroz Gonzalez
Department of Computer Science ...allegra!rochester!quiroz
University of Rochester or
Rochester, NY 14627 quiroz@cs.rochester.edu
alex@umbc3.UMD.EDU (Alex S. Crain) (12/15/87)
In article <2126@ulowell.cs.ulowell.edu> ross@swan.cs.ulowell.edu (Ross Miller) writes: > What if we had an implemenation of GKS for lisp. [much deleted about how programs that use incorrect data give poor results] [Lessee, I think I got a match here somewhare...there we go] I think that the problem here is that the wrong language is being used. Might I suggest VAX/VMS Pascal? VAX Pascal offers a multitude of idiot-proofing schemes to protect incompetant programmers from themselves, includeing run time bounds checking and full checking of types, use checking of variables, etc. True, all that extra work causes the code to run at about 1/2 speed at best, but it seems to supply what you want. I play consultant at the first-aide desk in the computer center a few hours a week, for the sake of the distraction, and I get mutitudes of people (more seniors than I would like) with the attitude that the language is responsibe for detection of programmer stupidity. My answer to them is the same as the one above. If you can't hack working without a net, get a net and suffer the consiquences of poor run time performance and code restriction. But don't blame the language for your mistakes. [flame off] There is a valid point here though, in that it is in general difficult to find obscure semantic errors in an interpreted environment. I was thinking is what I really want is lint for lisp. I was tracing through some code (mine) recently and found.. (prog (progn (arf narf snarf) as opposed to (arf narf snarf) ... ... ) ) which didn't cause major problems, but the variable scoping was wrong and (arf ...) was never evaled. I would have rather had something major. I would guess that lint for lisp wouldn't be too far fetched, a utility that would complain about unused variables and wrong # of arguments, etc. My compiler does this but that is so much overhead that its a bit extreme. Has anyone ever seen such an animal? Realizing that it would have to be tuned for each dialect, source for such a thing in any form that could be modified for use with CL would certainly be a help. :alex. alex@umbc3.umd.edu
ram@WB1.CS.CMU.EDU (Rob MacLachlan) (12/16/87)
>Wrong. Crreeeeaaaak, sound of Common lisp tomb being opened yet again,
Eeeeee-hah! What fun! Even MMister JJacobs has crawled back on the net,
dragging his odious slime-trail behind him. But I won't lower myself by
responding to his message. Instead I will flame about compilers and
declarations in Common Lisp, which Common Lisp fans know is one of my
favorite topics.
First of all, I point out that Common Lisp is a standard for writing
portable programs. It is possible to write programs that run in particular
Common Lisp implementation, yet are not Common Lisp programs.
The declaration mechanism in Common Lisp provides a way to make many
interesting assertions about a program. As a special case, it allows
programmers to make untrue assertions.
There are a number of areas in which "Common Lisp the Langauge" is vague
about what constitutes a correct Common Lisp program, but the aspect of type
declarations currently under discussion is not one of them.
I think the real complaint here is:
"Common Lisp has features that support unsafe (and efficient) compilation."
This is true, but it should be up to the programmer to decide whether he
wants to risk unsafe compilation. This is why Common Lisp proves the
OPTIMIZE declaration -- to allow the programmer to tell the compiler how to
evaluate speed/space/safety/compilation-speed tradeoffs.
In the abstract, the Common Lisp OPTIMIZE declaration provides better
control over these tradeoffs than any other Lisp dialect I know of.
Unfortunately, as realized by existing implementations, this capability is
largely lacking.
The most unfortunate property of existing Common Lisp compilers is their
assumption that a type declaration implies the generation of unsafe code.
There is nothing in Common Lisp that suggests this: it is merely an evil
vestiage of traditional Lisp compilation. I believe that a Common Lisp
compiler should have some mode in which it actually emits extra code to
verify all type declarations. This is a policy decision, controlled by the
OPTIMIZE declaration, and not an issue of language semantics.
Even without full type checking, the Common Lisp programmer is no worse off
than the programmer in any other dialect. He can avoid the risk of
incorrect type assertions by not making any.
The real reason that people flame about Common Lisp portability is a
paradoxical one: Common Lisp is the first Lisp dialect that has been
implemented many different times. People move their "Common Lisp" programs
from one implementation to another, expecting them to run without a hitch.
When they hit a snag, they snivel and whine. They never had this problem
with XYZZYLisp (of course, it only ran on one machine).
I won't argue that it is impossible (or even difficult) to come up with a
Lisp dialect that makes it harder to write non-portable code. Type
declarations are an obvious source of non-portability; they were included in
Common Lisp because efficiency was also a major goal.
If anyone has suggestions for ways to make Common Lisp more portable without
serious adverse effects on other design goals, then we would be glad to hear
them. It is not acceptable to either require declarations to be checked or
to forbid declarations, since both would cause serious efficiency problems.
Common Lisp can strongly suggest that the compiler offer a mode which does
full type checking, but it would be inconsistent with the language design
philosophy to require it. In Common Lisp terms, this is an "environment
issue". A given implementation can do whatever it wants to help programmers
write correct programs, but once written, these programs should run in an
environment that implmements only the features described in the manual.
Rob MacLachlan (ram@c.cs.cmu.edu)
CMU Common Lisp -- Making the world safe for symbolic computing.
jeff@aiva.ed.ac.uk (Jeff Dalton) (12/20/87)
In article <2126@ulowell.cs.ulowell.edu> ross@swan.cs.ulowell.edu (Ross Miller) writes: >An implementation is free to ignore type checking and not return any errors, >but it must except the code that declares type checking. That is not a >quote, but it is the jist. Here is a case where a program could be perfectly >debugged and still fail. Here is a way to think about type checking in Common Lisp: * Assume there is no type checking unless you explicitly code it by writing calls to CHECK-TYPE or your own tests and error calls. This assumption may well be correct for compiled code in some implementations. Note that CHECK-TYPE is a way to signal an error, not a declaration. An implementation is not free to ignore it. * Write your code with enough explicit checks so that users cannot confuse it with incorrect data. Use any additional internal checks that you feel are needed. Here I am assuming that only certain routines will be part of the external interface. You can enforce this by using packages (yes, I know one *can* still call internal procedures) or by using FLET and LABELS to keep internal routines local. * In cases where you are sure the types are correct, and where the speed of the compiled code is important, add declarations to inform the compiler of the types involved. It may then generate type-specific but more efficient code. Since you have actually checked the types somewhere, and have done a bit of type inference, you can be confient that the declarations are safe. Code of this sort will be portable. >On another subject have I missed the argument on why arrays are not >always passed by value? Are they sometimes not passed by value? Note that in Lisp objects, such as arrays, aren't copied when passed as parameters: in another language, they would be pointers. Jeff Dalton, JANET: J.Dalton@uk.ac.ed AI Applications Institute, ARPA: J.Dalton%uk.ac.ed@nss.cs.ucl.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!J.Dalton
roberts@cognos.uucp (Robert Stanley) (12/24/87)
In article <2126@ulowell.cs.ulowell.edu> ross@swan.cs.ulowell.edu (Ross Miller) writes: >Wrong. Crreeeeaaaak, sound of Common lisp tomb being opened yet again, I >have read and read, and reread that section looking for a way out. An >implementation is free to ignore type checking and not return any errors, >but it must except the code that declares type checking. That is not a ^ ^ | `- EXCEPT, meaning make an exception == not accept? `---- ACCEPT, indicating text is a misprint? >quote, but it is the jist. Here is a case where a program could be perfectly >debugged and still fail. Sorry, it's Christmas eve, I'm waiting for the compiler guys to finish printing 86 separate source files, and I read the above statement several times before admitting that I couldn't discriminate. >-- >csnet: ross@ulowell.cs.ulowell.edu >uucp: ross@ulowell.cs.ulowell.edu || ...harvard!ulowell!ross >Trust the computer. The computer is your friend. Yah, computers never fai -- R.A. Stanley - roberts@cognos.uucp uucp: decvax!utzoo!dciem!nrcaer!cognos!roberts Compuserve: 76174,3024 baud, bawd, bard, bared, bored, bore, bode, bade, bare, bar, boar, board intelligence at 1200 bps!
shebs%defun.utah.edu.uucp@utah-cs.UUCP (Stanley T. Shebs) (01/21/88)
In article <2126@ulowell.cs.ulowell.edu> ross@swan.cs.ulowell.edu (Ross Miller) writes: >[...] By accident he sends a float to polyline when an integer >was expected. Lisp then passes the 32bit word down to say C, machine size >may vary, and C blithly trys to write out a few million polylines, or some >other random number or worse a negative array reference, and the OS >informs us of an array overflow. [...] If anybody is surprised by all this, mail me and I'll send you a catalog of palaces in England I've been handling. There has been talk of doing "safe" interfaces between Lisp and C, but as long as I can do *(12345) in the address space of my Lisp system, nothing is safe. I can get a little help by using an interface that checks types, but it's going to be a dog, no way around it. > On another subject have I missed the argument on why arrays are not >always passed by value? Oh yeah, I like making copies of million-byte arrays every time I pass them to a function. Just think, I would get a copy with every call to AREF! That would show those purely functional weenies what *real* storage allocation is like! :-) :-) stan shebs shebs@cs.utah.edu