[comp.lang.lisp] Correctness

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 :-)