From: Dave Goel <deego3@gmail.com>
To: Miles Bader <miles@gnu.org>
Cc: Dave Goel <deego3@gmail.com>, emacs-devel@gnu.org
Subject: Re: Accepting and returning multiple values in 'cl
Date: Thu, 12 Mar 2009 12:25:20 -0400 [thread overview]
Message-ID: <87wsauiu8f.fsf@marie.gnufans.net> (raw)
In-Reply-To: <buotz5zs7sh.fsf@dhlpc061.dev.necel.com> (Miles Bader's message of "Thu, 12 Mar 2009 13:07:42 +0900")
> 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
next prev parent reply other threads:[~2009-03-12 16:25 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
[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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87wsauiu8f.fsf@marie.gnufans.net \
--to=deego3@gmail.com \
--cc=emacs-devel@gnu.org \
--cc=miles@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.