[Top][All Lists]

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

Re: Accepting and returning multiple values in 'cl

From: Dave Goel
Subject: Re: Accepting and returning multiple values in 'cl
Date: Thu, 12 Mar 2009 12:25:20 -0400
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

> 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 <address@hidden>
;; 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
;; 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))

(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
                `(let ((cl-internal-multiple nil)
                   (setq ,temp ,(nth ii args))
                       (listp cl-internal-multiple)
                       (cons ,temp cl-internal-multiple)

(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))))
           ,(loop for ii from  0 to (- l1 1) 
                  `( ,(nth ii vars) (nth ,ii ,temp)))

(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--")))
         ((,temp (cl-internal-multiple-values-obtain ,form)))
       (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) 
                    `(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 ()
  (let ( a b c d e f)
        (a b c)
      (values-list '(1 2 3)))
        (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 ()
  (let ((a 0) b c d)
        (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 ()
  (let ((sum 
          (values 1 2 3)
          (values 5 6 7))))
    (message "26: %s" sum)))

(defun cl-internal-test-4-list ()
  (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 ()
  (let (a b c d e f)
        (a b c d)
       (values 1 2 3 4)
       (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 ()
      ((fmy (&rest args)
              (values 1 2 3)
              4 5 6
              (values-list args)))
       (gmy (&rest args)
            (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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]