13

Is it possible to assign multiple return values directly to variables without going through a temporary variable in Emacs Lisp?

For example, let's say I have a function that returns a list of two lists:

(defun test-func ()
  (setq a '(a b))
  (setq b '(c d))
  `(,a ,b))

If I want to assign the first return value to list-a and the second return value to list-b, I can do this by using a temporary variable temp, for example:

(let* ((temp (test-func)) (list-a (car temp)) (list-b (cadr temp)))
  (message-box (prin1-to-string list-a))
  (message-box (prin1-to-string list-b)))

Is it possible to do this more simply? (I am used to Perl and Python where you do not have to specify a temporary variable)

Drew
  • 75,699
  • 9
  • 109
  • 225
Håkon Hægland
  • 3,608
  • 1
  • 20
  • 51
  • 2
    You may try `cl-destructuring-bind` macro. Also, did you really intend to use `setq` inside a `defun`? `setq` creates a "special" (globally accessible) variable, something you'd typically put outside a function (because there's little meaning in declaring the same variable more than once, while functions are intended to be run more than once). – wvxvw Jan 26 '15 at 10:54
  • @wvxvw Thanks! Yes I forgot to use `let` inside the function.. I did not plan to set any global variables :) – Håkon Hægland Jan 26 '15 at 11:23

2 Answers2

8

Common Lisp has a special facility - multiple values, and Emacs Lisp compatibility library emulates them using lists.

Thus you can do

(defun test-fun ()
  (let ((a 1) (b 2))
    (cl-values a b)))

(cl-multiple-value-bind (a b) (test-fun)
  ...)

(load cl-lib and use the cl- prefix for all CL functionality in EL).

NB: if you look at the SO answer linked above, you will see that emulating MV with lists is, to put it mildly, suboptimal (see also @Stefan's comment below).

sds
  • 5,928
  • 20
  • 39
  • Is there any advantage of using `multiple-value-bind` instead of `cl-multiple-value-bind` (only the latter seems to be documented in the manual https://www.gnu.org/software/emacs/manual/html_node/cl/Multiple-Values.html ) ? – Håkon Hægland Jan 26 '15 at 11:50
  • 3
    @HåkonHægland They are the same function, but you should use the **latter**. The `cl` package is not meant to be used anymore. You should always use the `cl-lib` package instead, which defines functions with the `cl-` prefix.. – Malabarba Jan 26 '15 at 11:54
  • 1
    I'd recommend against the use of `cl-values`: it's a "best effort" emulation of CommonLisp's `values` but it's not really compatible since all it does is return a list (i.e. it's kind of a lie), and in my experience people sooner or later end up manipulating those as lists (i.e. breaking the abstraction): better use lists explicitly (and if you don't like `pcase-let`, then use `cl-destructuring-bind` rather than `cl-multiple-value-bind`). – Stefan Jul 09 '18 at 15:59
5

Beside relying on the cl-lib compatibility package, the recommended way in Elisp for that is to use pcase:

(defun test-fun
  (let ((a '(a b))
        (b '(c d)))
    `(,a ,b)))

(defun other-test-fun ()
  (pcase-let ((`(,a ,b) (test-fun)))
    (message "a = %s; b = %s" a b)))

Beside pcase-let, there's also pcase-dolist, pcase-lambda, and pcase itself.

Stefan
  • 26,154
  • 3
  • 46
  • 84