[comp.lang.lisp] Multiple Return values in Common Lisp.

geller@sunybcs.UUCP (12/02/86)

I don't consider myself a great expert on Common Lisp, but
it seems to me that the idea of "multiple return values" of 
Common Lisp is ill conceived.  This is not to say that one 
does not need multiple return values, but that they should be
used in a conceptually different way.

Lets assume that the function "extent" returns an x value as
major return value, and a y value as "multiple".  Now lets assume that
I have an operation that needs both, the x and the y value.

In the current implementation of multiple return values I would have
to create a list of all return values and then access the x and y coordinates
from this list.

It would be much more reasonable to create a calling convention that
permits to access the different return values at different points.

Example:

          (defun extent (rectangle-name)

;;            This returns the x-length of the rectangle "rectangle-name"
;;            It also returns the y-length as a multiple
;;            return value.

               .......)


          (defun perimeter (rectangle-name)

;;              This uses both, the "normal" and the "multiple"
;;              return value.  It computes the perimeter of the
;;              rectangle "rectangle-name".

                 (+ (* (extent/1 rectangle-name) 2)
                    (* (extent/2 #*) 2)))

Now what is the postulated meaning of above notation?

      (extent/1 rectangle-name)

is a call to the "extent" function.  It returns the major
return value only.  That is, it returns the "x-extension" of
the rectangle "rectangle-name".

      (extent/2 #*) 

returns the "multiple" return value of THE SAME ARGUMENT as extent/1.
So in fact the extent function is NOT executed again, but the second
return value of the last call was stored at the time when extent/1
was called, and the call to (extent/2 #*) just retrieves this value.

The symbol /1 is meant to retrieve the major return value.
           /2 is meant to retrieve the second return value.
           #* is a pseudo argument that inhibits reexecution of the
           function.

I should mention that I am not particularly in love with my notation.
Any suggestion for a better notation will be accepted.  

Its just that I had to write a number of functions lately that
would have benefited from above mechanism. 

-------------------------------------------------------------------

The above concept is also applicable to "normal" functions,
although this is a minor concern for me at the current moment.

Example:

         (defun square-of-hard-to-get-number (argument)

             (* (long-computation argument)
                (long-computation #*)))

People who don't hesitate to use a "setq" will not understand
why I don't write:

         (defun square-of-hard-to-get-number (argument)

             (* (setq dummy (long-computation argument))
                dummy))

But I have been raised in the tradition of functional programming.

So I end up writing 

          (defun helper (argument)
              (square-variant (long-computation argument))) 

          (defun square-variant (hard-to-get-number)
              (* hard-to-get-number hard-to-get-number))

Or a lambda expression with the same effect.  This is really
an unnecessary inconvinience.

                        Jim

P.S.  Should this subject have been brought up already, my apology.
      I missed some news over diverse breaks.




===========================================================================
|| UUCP   : {cmc12,hao,harpo}!seismo!rochester!rocksvax!sunybcs!geller   ||
||          ...{allegra,decvax,watmath}!sunybcs!geller                   ||
|| CSNET  :  geller@buffalo                                              ||
|| ARPA   :  geller%buffalo@csnet-relay                                  ||
|| BITNET :  geller@sunybcs                                              ||
|| US MAIL:  James Geller/ Dept. of Comp. Sci./ SUNY at Buffalo/         ||
||           226 Bell Hall/ N.Y. 14260                                   ||
===========================================================================
                                     o
                                    o o
                                   o   o
                                  o     o

tsf@theory.cs.cmu.edu (Timothy Freeman) (12/04/86)

In article <1560@sunybcs.UUCP> geller@sunybcs.UUCP (James Geller) writes:
>I don't consider myself a great expert on Common Lisp, but
>it seems to me that the idea of "multiple return values" of 
>Common Lisp is ill conceived.
...
>          (defun perimeter (rectangle-name)
>                 (+ (* (extent/1 rectangle-name) 2)
>                    (* (extent/2 #*) 2)))
>

This can be written reasonably well in common lisp already as follows:

(defun perimeter (rectangle-name)
   (multiple-value-bind (x y) (extent rectangle-name)
      (+ (* x 2) (* y 2))))

>         (defun square-of-hard-to-get-number (argument)
>             (* (long-computation argument)
>                (long-computation #*)))
>

This can be written in common lisp as follows:

(defun square-of-hard-to-get-number (argument)
   (let ((res (long-computation argument)))
      (* res res)))

Using the proper binding constructs makes common lisp (or any other
variety of lisp) much more pleasant to use.
--
Tim Freeman

Arpanet: tsf@theory.cs.cmu.edu
-- 
Tim Freeman

Arpanet: tsf@theory.cs.cmu.edu

ram@spice.cs.cmu.edu (Rob MacLachlan) (12/04/86)

>From: geller@sunybcs.UUCP (James Geller)
>Newsgroups: comp.lang.lisp
>Subject: Multiple Return values in Common Lisp.
>
>It would be much more reasonable to create a calling convention that
>permits to access the different return values at different points.

What about multiple-value-bind?  This is the mv-receiving form used 99% 
of the time. 

In your example of non-mv use of "#*", I would just use LET.  This is a
perfectly acceptable aplicative alternative to SETQ.  If there is any major
efficency difference, your compiler is broken.  By the way, #* is already 
bit-vector syntax.

  Rob

king@kestrel.ARPA (Dick King) (12/04/86)

   From: geller@sunybcs.UUCP (James Geller)
   Newsgroups: comp.lang.lisp
   Date: 2 Dec 86 16:52:34 GMT
   Sender: nobody@sunybcs.UUCP
   Reply-To: geller@sunybcs.UUCP (James Geller)

   I don't consider myself a great expert on Common Lisp, but
   it seems to me that the idea of "multiple return values" of 
   Common Lisp is ill conceived.  This is not to say that one 
   does not need multiple return values, but that they should be
   used in a conceptually different way.

I disagreee that it's as bad as you think.


   Example:

	     (defun extent (rectangle-name)

   ;;            This returns the x-length of the rectangle "rectangle-name"
   ;;            It also returns the y-length as a multiple
   ;;            return value.

		  .......)


	     (defun perimeter (rectangle-name)

   ;;              This uses both, the "normal" and the "multiple"
   ;;              return value.  It computes the perimeter of the
   ;;              rectangle "rectangle-name".

		    (+ (* (extent/1 rectangle-name) 2)
		       (* (extent/2 #*) 2)))

This would be better rendered

  (multiple-value-bind (length width) (extent rectangle-name)
      (+ (* length 2) (* width 2)))


   Example:

	    (defun square-of-hard-to-get-number (argument)

		(* (long-computation argument)
		   (long-computation #*)))

   People who don't hesitate to use a "setq" will not understand
   why I don't write:

	    (defun square-of-hard-to-get-number (argument)

		(* (setq dummy (long-computation argument))
		   dummy))

   But I have been raised in the tradition of functional programming.

   So I end up writing 

	     (defun helper (argument)
		 (square-variant (long-computation argument))) 

	     (defun square-variant (hard-to-get-number)
		 (* hard-to-get-number hard-to-get-number))

It's better to write

(defun square-of-grody-comp (n)
   (let ((val (long-computation n)))
      (* val val)))


   Or a lambda expression with the same effect.  This is really
   an unnecessary inconvinience.

			   Jim

   P.S.  Should this subject have been brought up already, my apology.
	 I missed some news over diverse breaks.

-dick

shebs@utah-cs.UUCP (Stanley Shebs) (12/05/86)

For those who need academic approval for features of Common Lisp, I offer
Peter Henderson's book "Functional Programming: Application and
Implementation", page 269:

"For practical purposes, however, ... our purely functional language [is]
in fact very rudimentary, and some very simple extensions would greatly
enhance its ability to express clearly certain classes of computation.
... That purely syntactic extensions can have great power, in terms of
improved clarity of expression, is illustrated by the extension which we
now undertake."

"We extend our purely functional language to allow functions to return
multiple results. ..."

He then goes on to describe some constructs which are essentially VALUES
and MULTIPLE-VALUE-BIND in CL.  CL's multiple values are somewhat more
complicated, partly because it needs to work with the rest of the language,
and partly because Henderson's version has some undesirable limitations
(must always have exact match of multiplicities, for instance).

							stan

jbs@mit-eddie.MIT.EDU (Jeff Siegal) (12/06/86)

In article <1560@sunybcs.UUCP> geller@sunybcs.UUCP (James Geller) writes:
>[...]
>It would be much more reasonable to create a calling convention that
>permits to access the different return values at different points.
>
>Example:
>
>          (defun extent (rectangle-name)
>[comments omitted]
>               .......)
>
>          (defun perimeter (rectangle-name)
>[comments omitted]
>                 (+ (* (extent/1 rectangle-name) 2)
>                    (* (extent/2 #*) 2)))
>
>Its just that I had to write a number of functions lately that
>would have benefited from above mechanism. 

As has been mentioned before, multiple-value-bind is the probable
solution

>Example:
>
>         (defun square-of-hard-to-get-number (argument)
>             (* (long-computation argument)
>                (long-computation #*)))
>
>People who don't hesitate to use a "setq" will not understand
>why I don't write:
>
>         (defun square-of-hard-to-get-number (argument)
>
>             (* (setq dummy (long-computation argument))
>                dummy))
>
>But I have been raised in the tradition of functional programming.
>
>[code omitted]

To see why wanting the #* form is not consistent with functional
programming, let's look at an (sub-optimal) implementation (yes,
you may use this in your own programs):

(setq *applyhook*
  #'(lambda (funct args env)
      (if (symbolp funct)
	  (let ((last-call-result-symbol
		 (intern (concatenate "prev-call-result-#"
				      (print-name funct)))))
	    (if (eq (car args) '|#*|)
		(let ((*applyhook* nil))
		  (symbol-value last-call-result-symbol))
	        (set last-call-result-symbol 
		     (applyhook funct
				args
				*evalhook*
				*applyhook*
				env)))))))

Now, what do we find within this code?  None other than "set", which
is what you were trying to avoid.  You've removed the state variable
from the program by putting it into the interpreter.

If anyone does decide to use (or even test) this code, please 
let me know, since I didn't actually test it.

Jeff Siegal

jbs@mit-eddie.MIT.EDU (Jeff Siegal) (12/06/86)

In article <1560@sunybcs.UUCP> geller@sunybcs.UUCP (James Geller) writes:
>[...]
>It would be much more reasonable to create a calling convention that
>permits to access the different return values at different points.
>
>Example:
>
>          (defun extent (rectangle-name)
>[comments omitted]
>               .......)
>
>          (defun perimeter (rectangle-name)
>[comments omitted]
>                 (+ (* (extent/1 rectangle-name) 2)
>                    (* (extent/2 #*) 2)))
>
>Its just that I had to write a number of functions lately that
>would have benefited from above mechanism. 

As has been mentioned before, multiple-value-bind is the probable
solution

>Example:
>
>         (defun square-of-hard-to-get-number (argument)
>             (* (long-computation argument)
>                (long-computation #*)))
>
>People who don't hesitate to use a "setq" will not understand
>why I don't write:
>
>         (defun square-of-hard-to-get-number (argument)
>
>             (* (setq dummy (long-computation argument))
>                dummy))
>
>But I have been raised in the tradition of functional programming.
>
>[code omitted]

To see why wanting the #* form is not consistent with functional
programming, let's look at an (sub-optimal) implementation (yes,
you may use this in your own programs):

(defun apply-with-prev-return (funct args env)
  (if (symbolp funct)	
      (let ((last-call-result-symbol
	     (intern (concatenate "prev-call-result-#"
				  (print-name funct)))))
	(if (not (eq (car args) '|#*|))
	    (set last-call-result-symbol 
		 (multiple-value-list
		  (applyhook funct
			     args
			     nil
			     #'apply-with-prev-return
			     env))))
	(values-list (symbol-value last-call-result-symbol)))
      (applyhook funct
		 args
		 nil
		 #'apply-with-prev-returh
		 env)))
(setq *applyhook* #'apply-with-prev-return)

Now, what do we find within this code?  None other than "set", which
is what you were trying to avoid.  You've removed the state variable
from the program by putting it into the interpreter.

If anyone does decide to use (or even test) this code, please let me
know of any bugs, etc., since I didn't actually test it.

Jeff Siegal