* Accepting and returning multiple values in 'cl @ 2009-03-12 3:32 Dave Goel 2009-03-12 4:07 ` Miles Bader 0 siblings, 1 reply; 8+ messages in thread From: Dave Goel @ 2009-03-12 3:32 UTC (permalink / raw) To: emacs-devel, D. Goel Is devel the correct upstream place to make/discess changes to 'cl? It seems to me that CL can easily handle multiple values correctly. Currently, it uses lists instead of multiple values, which is far less than satisfactory. Just to cite one example, a user should be able to use both these simultaneously: (setq a (floor* 1 2)) as well as (multiple-value-setq (a b) (floor* 1 2)) ---- I am including below a file that I think implements every single multiple value facility in the common lisp spec. No codewalking hacks or anything. Just macros. Not only that, I believe every facility's detailed spec is implemented to the dot. For example, everything is evalled in the proper order it should be and only if it should be, multiple-value-setq carefully does the right thing when the number asked and supplied differ. Zero values (values) are handled correctly, etc. At the end follow 4 tests. After some doc. cleanup, we can make it a part of emacs' cl. Please let me know of any feedback. Dave -- ;;; cl-multiple.el --- Framework to handle multiple values in emacs. ;; Time-stamp: <2009-03-11 23:21:33 deego> ;; Copyright (C) 2009 Dave Goel ;; Emacs Lisp Archive entry ;; Filename: cl-multiple.el ;; Package: cl-multiple ;; Author: Dave Goel <deego3@gmail.com> ;; Keywords: ;; Version: dev ;; This file is NOT (yet) part of GNU Emacs. ;; This is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. (require 'cl) (defconst multiple-values-limit most-positive-fixnum "Our implementation has no internal limit.") ;; This variable should never be used by anything except values. (defvar cl-internal-multiple nil "Internal variable. Every multiple-value-acceptor binds this variable to to nil before calling each of its arguments. When a call to values returns one value, it leaves this variable as nil. When a call to values returns > 1 value, it sets this variable equal to the cdr of the list of its returned values. When a call to values returns 0 values, it sets this varible to nil.") ;; These functions are not defined in regular emacs anyway. So, if ;; someone calls them, we can afford to autoload these. Regular cl ;; seems to autoload them too. todo: double-check. ;;;###autoload (defun values-list (ls) (let ((a1 (car ls))) (setq cl-internal-multiple (if ls (cdr ls) 0)) a1)) ;;;###autoload (defun values (&rest args) (values-list args)) ;; The rest of the functions simply need to accept things properly. ;; They can employ this one function for help! (defmacro cl-internal-multiple-values-obtain (&rest args) "As a test case, try to call this on three args, each providing values." (let* ((max (- (length args) 1)) (temp (gensym "--cl-var--"))) (cons 'list (loop for ii from 0 to max collect `(let ((cl-internal-multiple nil) ,temp) (setq ,temp ,(nth ii args)) (if (listp cl-internal-multiple) (cons ,temp cl-internal-multiple) nil)))))) ;;;==================================================== ;; Now, we can code everything else. ;;;###autoload (defmacro multiple-value-bind (vars form &rest body) ;; This itself needs to only return one value. ;; We do need to eval ALL forms, but set only as many as supplied in ;; vars. (let ((temp (gensym "--cl-var--")) ;;(temp2 (make-symbol "--cl-var--")) (l1 (length vars))) `(let ((,temp (first (cl-internal-multiple-values-obtain ,form)))) (let ,(loop for ii from 0 to (- l1 1) collect `( ,(nth ii vars) (nth ,ii ,temp))) ,@body)))) ;;;###autoload (defmacro multiple-value-call (fn &rest forms) (let ((temp (gensym "--cl-var--"))) `(let ((,temp (cl-internal-multiple-values-obtain ,@forms))) (apply ,fn (apply #'append ,temp))))) ;;;###autoload (defmacro multiple-value-list (form) `(car (cl-internal-multiple-values-obtain ,form))) ;;;###autoload (defmacro multiple-value-prog1 (form &rest others) (let ((temp (gensym "--cl-var--"))) `(let ((,temp (cl-internal-multiple-values-obtain ,form))) ,@others ;;(values-list (car ,temp))))) (values-list (car ,temp))))) ;;;(values-list (car ,temp))))) ;;;###autoload (defmacro multiple-value-setq (vars form) ;; We do need to eval ALL forms, but set only as many as supplied in ;; vars. (let ((temp (gensym "--cl-var--")) (l1 (length vars))) `(let ((,temp (first (cl-internal-multiple-values-obtain ,form)))) ,(cons 'prog1 (loop for ii from 0 to (- l1 1) collect `(setf ,(nth ii vars) (nth ,ii ,temp))))))) ;;;==================================================== ;; These functions in cl-extra should now be tweaked to return ;; (values) instead of (list). Until now, values was == list, so it ;; was ok for them to use (list): ;; floor*, ceiling*, truncate*, round*, mod*. ;; tests. (defun cl-internal-test-1-values-values-list-and-setq () (interactive) (let ( a b c d e f) (multiple-value-setq (a b c) (values-list '(1 2 3))) (multiple-value-setq (d e f) (values 4 5 6)) (message "1 2 3 4 5: %s %s %s %s %s %s" a b c d e f))) (defun cl-internal-test-2-bind () (interactive) (let ((a 0) b c d) (multiple-value-bind (a b c d) (values 1 2 3 4) (setq b c) (setq d a) (message "1 3 3 1: %s %s %s %s .. sleeping for 2" a b c d)) (sit-for 2) (message "0 nil nil nil nil: %s %s %s %s" a b c d))) (defun cl-internal-test-3-call () (interactive) (let ((sum (multiple-value-call #'+ (values 1 2 3) ;;(values) 2 (values 5 6 7)))) (message "26: %s" sum))) (defun cl-internal-test-4-list () (interactive) (let ((a (multiple-value-list (values))) (b (multiple-value-list (values 1 2 3))) (c (multiple-value-list 4)) (d (multiple-value-list 5))) (message "nil (1 2 3) (4) (5): %s %s %s %s" a b c d))) (defun cl-internal-test-5-prog1 () (interactive) (let (a b c d e f) (multiple-value-setq (a b c d) (multiple-value-prog1 (values 1 2 3 4) nil (setq e 5) (values 5 6 7 8))) (message "1 2 3 4 5: %s %s %s %s %s" a b c d e))) (provide 'cl-multiple) (run-hooks 'cl-multiple-after-load-hook) ;;; cl-multiple.el ends here ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Accepting and returning multiple values in 'cl 2009-03-12 3:32 Accepting and returning multiple values in 'cl Dave Goel @ 2009-03-12 4:07 ` Miles Bader 2009-03-12 4:11 ` Dave Goel 2009-03-12 16:25 ` Dave Goel 0 siblings, 2 replies; 8+ messages in thread From: Miles Bader @ 2009-03-12 4:07 UTC (permalink / raw) To: Dave Goel; +Cc: emacs-devel Dave Goel <deego3@gmail.com> writes: > It seems to me that CL can easily handle multiple values correctly. > Currently, it uses lists instead of multiple values, which is far less > than satisfactory. Just to cite one example, a user should be able to > use both these simultaneously: I'm not sure it's worth trying to develop the cl.el MRV stuff much unless it actually works like real CL MRVs: (1) _efficient_, no consing (2) seamlessly interoperable with SRVs (e.g., non-MRV-aware callers can just pretend a MRV function returns a single value, with no effort on their part) -Miles -- You can hack anything you want, with TECO and DDT. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Accepting and returning multiple values in 'cl 2009-03-12 4:07 ` Miles Bader @ 2009-03-12 4:11 ` Dave Goel 2009-03-12 4:41 ` Jonathan Rockway 2009-03-12 16:25 ` Dave Goel 1 sibling, 1 reply; 8+ messages in thread From: Dave Goel @ 2009-03-12 4:11 UTC (permalink / raw) To: Miles Bader; +Cc: Dave Goel, emacs-devel > > I'm not sure it's worth trying to develop the cl.el MRV stuff much > unless it actually works like real CL MRVs: > Please ignore my post. Sorry for wasting your time. My "implementation" has problems. I now know why I had this feeling it just couldn't have been so easy. :-) ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Accepting and returning multiple values in 'cl 2009-03-12 4:11 ` Dave Goel @ 2009-03-12 4:41 ` Jonathan Rockway 0 siblings, 0 replies; 8+ messages in thread From: Jonathan Rockway @ 2009-03-12 4:41 UTC (permalink / raw) To: emacs-devel * On Wed, Mar 11 2009, Dave Goel wrote: > Please ignore my post. Sorry for wasting your time. No need to apologize for writing code. If people were unwilling to try things and ask others for feedback, there would be no free software (or Emacs). Regards, Jonathan Rockway -- print just => another => perl => hacker => if $,=$" ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Accepting and returning multiple values in 'cl 2009-03-12 4:07 ` Miles Bader 2009-03-12 4:11 ` Dave Goel @ 2009-03-12 16:25 ` Dave Goel [not found] ` <jwvzlfqhawb.fsf-monnier+emacs@gnu.org> 1 sibling, 1 reply; 8+ messages in thread From: Dave Goel @ 2009-03-12 16:25 UTC (permalink / raw) To: Miles Bader; +Cc: Dave Goel, emacs-devel > I'm not sure it's worth trying to develop the cl.el MRV stuff much > unless it actually works like real CL MRVs: > > (1) _efficient_, no consing > > (2) seamlessly interoperable with SRVs (e.g., non-MRV-aware callers > can just pretend a MRV function returns a single value, with no > effort on their part) Miles, In response to these points: after further thinking, I have added a section on pros and cons at the beginning, and a new test example of the gotchas at the end. It seems that no modification to any SR oerations is needed, so point 2 is takesn care of. No efficiency is lost at all for the usual SR stuff eiter. When I asked to ignore my post, I had worried that the implementation suffered from a flaw, but with a simple convention that m-v users can follow, this can be overcome, and is much better than trying to modify all SR functions. This way of m-v handling seems preferable to me than the (list) way of doing things. Attaching the new file. ;;; cl-multiple.el --- Handle multiple return values in emacs. ;; ;; Time-stamp: <2009-03-12 12:23:07 deego> ;; Copyright (C) 2009 Dave Goel ;; Emacs Lisp Archive entry ;; Filename: cl-multiple.el ;; Package: cl-multiple ;; Author: Dave Goel <deego3@gmail.com> ;; Keywords: ;; Version: dev ;; This file is not part of GNU Emacs. ;; This is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; ;;; Cons, and gotchas: ;;; We don't want to change the usual single-value functions in emacs, ;;; so as to avoid any overhead. However, this can lead to funny ;;; situations, for example: ;;; ;;; (multiple-value-setq (a b) (progn (values 1 2) 3)) a and b should ;;; be set to 3 and nil respectively, but they are set to 3 and 2 ;;; respectively. ;;; The workaround is very simple for m-v users, and works no matter ;;; how deep you go in the stack. S-v users can continue to function ;;; normally in any case. ;;; Workaround: Any time you have a list of forms, the final form ;;; should always return its value(s) via (values). Moreover, this ;;; condition only need be satisfied only when one or more among the ;;; list of forms actually returns multiple (or zero) values. Thus, ;;; in the above example, the right thing can be done by: ;;; (multiple-value-setq (a b) (progn (values 1 2) (values 3))) ;;; If however, a list of your forms looks like: ;;; (progn 1 (list 1 2 ) 3 5), you need not modify your 5 to read ;;; (values 5), because nothing else in your form returned an ;;; abnormal number of values. ;;; Pros: ;;; (1) Better CL compatibility. ;;; (2) Suppose you choose to modify your existing function to ;;; return an extra value. Everything that depends on your function ;;; will break in the current emacs, because multiple values become ;;; lists. You will need to go and hunt for every such invocation of ;;; your function. But, not in this new way, which is much closer to ;;; the common lisp way. ;;; (3) Also, you can easily do things like these, which is currently not ;;; possible. If floor* returns (values) rather than a list, you can ;;; make *both* these work. Currently, only one works. ;;; ;;; (setq a (floor* 1 2)) ;;; (multiple-value-setq (a b) (floor* 1 2)) ;;; ;;; Single-value users can continu (require 'cl) (defconst multiple-values-limit most-positive-fixnum "Our implementation has no internal limit.") ;; This variable should never be used by anything except values. (defvar cl-internal-multiple nil "Internal variable. Every multiple-value-acceptor binds this variable to to nil before calling each of its arguments. When a call to values returns one value, it leaves this variable as nil. When a call to values returns > 1 value, it sets this variable equal to the cdr of the list of its returned values. When a call to values returns 0 values, it sets this varible to nil.") ;; These functions are not defined in regular emacs anyway. So, if ;; someone calls them, we can afford to autoload these. Regular cl ;; seems to autoload them too. todo: double-check. (defun values-list (ls) (let ((a1 (car ls))) (setq cl-internal-multiple (if ls (cdr ls) 0)) a1)) (defun values (&rest args) (values-list args)) (defmacro cl-internal-multiple-values-obtain (&rest args) "As a test case, try to call this on three args, each providing values." (let* ((max (- (length args) 1)) (temp (gensym "--cl-var--"))) (cons 'list (loop for ii from 0 to max collect `(let ((cl-internal-multiple nil) ,temp) (setq ,temp ,(nth ii args)) (if (listp cl-internal-multiple) (cons ,temp cl-internal-multiple) nil)))))) (defmacro multiple-value-bind (vars form &rest body) ;; This itself needs to return only one value. ;; We do need to eval ALL forms, but set only as many as supplied in ;; vars. (let ((temp (gensym "--cl-var--")) (l1 (length vars))) `(let ((,temp (first (cl-internal-multiple-values-obtain ,form)))) (let ,(loop for ii from 0 to (- l1 1) collect `( ,(nth ii vars) (nth ,ii ,temp))) ,@body)))) (defmacro multiple-value-call (fn &rest forms) (let ((temp (gensym "--cl-var--"))) `(let ((,temp (cl-internal-multiple-values-obtain ,@forms))) (apply ,fn (apply #'append ,temp))))) (defmacro multiple-value-list (form) `(car (cl-internal-multiple-values-obtain ,form))) (defmacro multiple-value-prog1 (form &rest others) (let ((temp (gensym "--cl-var--"))) `(let ((,temp (cl-internal-multiple-values-obtain ,form))) ,@others (values-list (car ,temp))))) (defmacro multiple-value-setq (vars form) ;; We do need to eval ALL forms, but set only as many as supplied in ;; vars. (let ((temp (gensym "--cl-var--")) (l1 (length vars))) `(let ((,temp (first (cl-internal-multiple-values-obtain ,form)))) ,(cons 'prog1 (loop for ii from 0 to (- l1 1) collect `(setf ,(nth ii vars) (nth ,ii ,temp))))))) ;;;==================================================== ;; These functions in cl-extra should now be tweaked to return ;; (values) instead of (list). Until now, values was == list, so it ;; was ok for them to use (list): ;; floor*, ceiling*, truncate*, round*, mod*. ;; tests. (defun cl-internal-test-1-values-values-list-and-setq () (interactive) (let ( a b c d e f) (multiple-value-setq (a b c) (values-list '(1 2 3))) (multiple-value-setq (d e f) (values 4 5 6)) (message "1 2 3 4 5: %s %s %s %s %s %s" a b c d e f))) (defun cl-internal-test-2-bind () (interactive) (let ((a 0) b c d) (multiple-value-bind (a b c d) (values 1 2 3 4) (setq b c) (setq d a) (message "1 3 3 1: %s %s %s %s .. sleeping for 2" a b c d)) (sit-for 2) (message "0 nil nil nil nil: %s %s %s %s" a b c d))) (defun cl-internal-test-3-call () (interactive) (let ((sum (multiple-value-call #'+ (values 1 2 3) (values) 2 (values 5 6 7)))) (message "26: %s" sum))) (defun cl-internal-test-4-list () (interactive) (let ((a (multiple-value-list (values))) (b (multiple-value-list (values 1 2 3))) (c (multiple-value-list 4)) (d (multiple-value-list 5))) (message "nil (1 2 3) (4) (5): %s %s %s %s" a b c d))) (defun cl-internal-test-5-prog1 () (interactive) (let (a b c d e f) (multiple-value-setq (a b c d) (multiple-value-prog1 (values 1 2 3 4) nil (setq e 5) (values 5 6 7 8))) (message "1 2 3 4 5: %s %s %s %s %s" a b c d e))) (defun cl-internal-test-6-gotchas () (interactive) (flet ((fmy (&rest args) (progn (values 1 2 3) 4 5 6 nil (values) 7 (values-list args))) (gmy (&rest args) 8 (values 9 10) (apply #'fmy (cdr args)))) (let ((hmy (multiple-value-list (gmy 12 13)))) (message "(13): %s" hmy)))) (provide 'cl-multiple) (run-hooks 'cl-multiple-after-load-hook) ;;; cl-multiple.el ends here ^ permalink raw reply [flat|nested] 8+ messages in thread
[parent not found: <jwvzlfqhawb.fsf-monnier+emacs@gnu.org>]
[parent not found: <87bps6ip3n.fsf@marie.gnufans.net>]
[parent not found: <jwv4oxyh74y.fsf-monnier+emacs@gnu.org>]
* Re: Accepting and returning multiple values in 'cl [not found] ` <jwv4oxyh74y.fsf-monnier+emacs@gnu.org> @ 2009-03-12 19:57 ` Dave Goel 2009-03-12 21:03 ` Stefan Monnier 0 siblings, 1 reply; 8+ messages in thread From: Dave Goel @ 2009-03-12 19:57 UTC (permalink / raw) To: Stefan Monnier; +Cc: Dave Goel, emacs-devel > The reason why I'm not sure we should install it, is that I'm afraid >people got used to the current quirks of MRV, and the quirks of your >alternative implementations aren't obviously preferable: yes, if we >started cl.el now, I might choose your code over the current one, but >I'm not sure it's worth the potential breakage now. I do see your point. I see a very few places in emacs, but it is understandable. Actually, I discovered cl's authors's own cl-compat.el, which is already in emacs, which already seeks to do the right thing (just what my cl-multiple.el did), with the alternative names like Values, Multiple-value-*, etc. I grepped through the whole emacs, nothing uses these functions. I see problems with cl-compat though, for example: My code: (multiple-value-list (progn (values nil) (values))) nil (Multiple-value-list (progn (Values nil) (Values))) (nil) ^^ that is wrong. Its implementation is also a bit funny and convoluted. It depends on cl.el's multiple-value- implementations, which itself is hairy. Do you mind if I fix these bugs with cl-compat.el and reimplement its m-v functions? There, I also want to add a note to the user: any time they use forms that return m- values, the final form should invoke (Values ) as well. Keeping with cl-compat's usual terminology, cl-floor* and cl-truncate* should be defaliased to Floor*, and Truncate*... It seems like these changes won't affect the rest of emacs at all. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Accepting and returning multiple values in 'cl 2009-03-12 19:57 ` Dave Goel @ 2009-03-12 21:03 ` Stefan Monnier 2009-03-13 19:05 ` Dave Goel 0 siblings, 1 reply; 8+ messages in thread From: Stefan Monnier @ 2009-03-12 21:03 UTC (permalink / raw) To: Dave Goel; +Cc: emacs-devel > Do you mind if I fix these bugs with cl-compat.el and reimplement its > m-v functions? Of course, not (tho, since we're in pretest, we won't be able to install such changes immediately). > Keeping with cl-compat's usual terminology, cl-floor* and cl-truncate* > should be defaliased to Floor*, and Truncate*... Actually, I'd rather increase the use of the "cl-" prefix than reduce it. Emacs's lack of modules makes it particularly important to use prefix wherever possible. Stefan PS: Now that I look at it, I think cl-compat.el should be moved to lisp/obsolete/. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Accepting and returning multiple values in 'cl 2009-03-12 21:03 ` Stefan Monnier @ 2009-03-13 19:05 ` Dave Goel 0 siblings, 0 replies; 8+ messages in thread From: Dave Goel @ 2009-03-13 19:05 UTC (permalink / raw) To: Stefan Monnier; +Cc: Dave Goel, emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >> Do you mind if I fix these bugs with cl-compat.el and reimplement its >> m-v functions? > > Of course, not (tho, since we're in pretest, we won't be able to > install such changes immediately). > I wanted to make my cl-multiple.el not overwrite emacs' default bindings - I believe that merely loading a library should not change the way a user's emacs functions.. So I capitalized everything, just as cl-compat does. ..with the end result being that it is a replacement for cl-compat. Its docs provide an easy way to get rid of the (rare) m-v gotchas. "the TWO RULES to make m-v work correctly". It has better error checking, warning, etc. For example, M-x Cl-internal-test-08-gotchas-warn, and its corrected version 8b. See also the function cl-multiple-install, and the 25 tests at the end. http://www.gnufans.net/~deego/emacspub/lisp-mine/cl-multiple/dev/cl-multiple.el ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2009-03-13 19:05 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2009-03-12 3:32 Accepting and returning multiple values in 'cl Dave Goel 2009-03-12 4:07 ` Miles Bader 2009-03-12 4:11 ` Dave Goel 2009-03-12 4:41 ` Jonathan Rockway 2009-03-12 16:25 ` Dave Goel [not found] ` <jwvzlfqhawb.fsf-monnier+emacs@gnu.org> [not found] ` <87bps6ip3n.fsf@marie.gnufans.net> [not found] ` <jwv4oxyh74y.fsf-monnier+emacs@gnu.org> 2009-03-12 19:57 ` Dave Goel 2009-03-12 21:03 ` Stefan Monnier 2009-03-13 19:05 ` Dave Goel
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.