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