[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Void



    Date: Sun, 11 May 1986  21:06 EDT
    From: Rob MacLachlan <RAM@C.CS.CMU.EDU>
    
    Well, I freely admit that the big hole in my proposal is defining
    what it means to "use" a VOID value.  I think that the best approach
    is to be extremely facist.

I've outlined reasonable design principles which suggest this is a bogus
thing do do. I hardly find your idea of ignoring those principles and
just arbitrarily pulling a semantics out of the air to be very convincing.

  The only places where a void expression may be legal are:
      1] Any place where the value is immediately discarded: PROGN, etc.
      2] Any place in a function that can return multiple values.  In
	 addition to tail-recursive positions, this includes the protected
	 form of UNWIND-PROTECT and the values form for
	 MULTIPLE-VALUE-PROG1 when these forms are in such a
	 multiple-value position. 
    
I'm sorry, but I find this completely ridiculous. Many valid programs can be
written which use `void' values in ways other than this without being ill-formed.

    Note that in either case, a void value may be illegal because the
    result was declared to be of some other type:

    (proclaim '(function frob-foo (foo) void))
    (defun frob-foo (foo) ...)
    
    (proclaim '(function make-foo ((member t nil)) foo))
    (defun make-foo (frob-p)
      (let ((foo (cons-a-foo)))
	(if frob-p
	    (frob-foo foo)
	    foo)))

Suppose that FROB-FOO puts the FOO on the heap somewhere. Then suppose that
I have a function, CREATE-FOO, which is the only call of MAKE-FOO and is 
defined as: 

(DEFUN CREATE-FOO (STASH-P)
  (COND (PUT-IN-HEAP 
	 (MAKE-FOO T)
	 (VALUES NIL NIL))
	(T
	 (VALUES (MAKE-FOO NIL) T))))

There's nothing ill-formed about the collection of programs which include
my program and yours.

    In this classic "oops, I forgot to return the value" example, the
    compiler is quite justified in giving a warning, since one branch of
    the IF can never be legally executed.  The function MAKE-FOO itself is
    not erroneous, but the compiler could replace the call to FROB-FOO
    with code that just signals an error.  When this happens, I would like
    my compiler to tell me that something may be wrong, since there almost
    certainly is.
    
The programming problem you're worried about is a common one, but the technique
you're proposing for fixing it is just not practical. This situation comes up
in legitimate code (especially when macros are involved) all the time.

Consider the following common situation: I have a macro MYBLOCK which binds
the variable * to the value of the previous computation at the same level.
For example:

(DEFMACRO MYBLOCK (&BODY FORMS)
  `(LET ((* *))
     ,@(MAPCAR #'(LAMBDA (FORM) `(SETQ * ,FORM)) FORMS)))

This lets me do:

(MYBLOCK 3 (+ * 5) (- * 2)) ==> 6

But what if the function FOO has a void return value? It's completely
reasonable to do:

(MYBLOCK (FOO) (BAR) (PRINT *))

because * gets assigned but never used. 

The way the program can represent the information necessary to decide whether a
value is used correctly are endless. You really cannot mechanically detect them.