Returning multiple values vs returning a list of data

Discussion of Common Lisp
Post Reply
garethw
Posts: 43
Joined: Fri Jul 13, 2012 12:56 pm
Location: Ottawa, ON

Returning multiple values vs returning a list of data

Post by garethw » Fri Feb 01, 2013 11:21 am

Sort of a philosophy question:

When I first learned of multiple return values, I thought it sounded like a great idea, but in most cases where I tried to use them, I found them ultimately awkward and less convenient than simply returning the values I need in a list or cons pair and letting the caller sort them out.

I'm curious if anyone could suggest a kind of pattern than might indicate where multiple return values might be useful.

The few places that I can name off the top of my head where CL functions return multiple values (floor, get-hash), seem to be cases where the work to produce one value essentially produces a second value "for free". Is that a good litmus test? Are there reasons one might return multiple values for another reason? Pure efficiency, maybe?
~garethw

Goheeca
Posts: 271
Joined: Thu May 10, 2012 12:54 pm
Contact:

Re: Returning multiple values vs returning a list of data

Post by Goheeca » Fri Feb 01, 2013 1:04 pm

Maybe they are awkward, but they make a companion part to multiple arguments on the call stack and I think they should be used in cases like a polar2rectangular function, although the treating with them is dirty, but we have macros to wrap/fix that.
Yes, I've missed better destructuring of multiple values few times and rahter I've used a list too.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.

sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

Re: Returning multiple values vs returning a list of data

Post by sylwester » Fri Feb 01, 2013 1:36 pm

The difference is that most implementation would utilize stack or registers to hold pointers to the returned value while you would have to cons and traverse the list when using lists.
Consider:

Code: Select all

(defun get-values ()
 (values 1 2 3 4))

(defun test ()
(multiple-value-bind (a2 b2 c2 d2) 
    (get-values)
    (+  a2 b2 c2 d2)))

(disassemble 'test) ==>
0     (CALL0 0)                           ; GET-VALUES
2     (NV-TO-STACK 4)
4     (LOAD&PUSH 3)
5     (LOAD&PUSH 3)
6     (LOAD&PUSH 3)
7     (LOAD&PUSH 3)
8     (CALLSR 4 55)                       ; +
11    (SKIP&RET 5)
While this would not be quite the same

Code: Select all

(defun get-values2 ()
  (list 1 2 3 4))

(defun test2 (a b c d)
 (let ((return (get-values2)))
   (+ (car return) (cadr return) (caddr return) (cadddr return))))

(disassemble 'test2) ==>
0     (CALL0 0)                           ; GET-VALUES2
2     (PUSH)
3     (LOAD&CAR&PUSH 0)
5     (LOAD 1)
6     (CDR)
7     (CAR&PUSH)
8     (LOAD 2)
9     (CDR)
10    (CDR)
11    (CAR&PUSH)
12    (LOAD 3)
13    (CDR)
14    (CDR)
15    (CDR)
16    (CAR&PUSH)
17    (CALLSR 4 55)                       ; +
20    (SKIP&RET 6)
In most circumstances this won't hurt much though, but if it does it's nice to be able to do the first and I think the code is easier to understand.
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

garethw
Posts: 43
Joined: Fri Jul 13, 2012 12:56 pm
Location: Ottawa, ON

Re: Returning multiple values vs returning a list of data

Post by garethw » Tue Feb 05, 2013 2:00 pm

Thanks as always for the replies. I do understand the difference between the two, sylwester - my question was about deciding which is more appropriate in a given context.

Goheeca's example of polar2rect is an interesting example. My inclination would have be to use a cons here, since the x and y coords would often travel around together, returned from one function, passed into the arguments of the next, but maybe along with others. Multiple return-values seem like they might get a bit awkward here.
~garethw

sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

Re: Returning multiple values vs returning a list of data

Post by sylwester » Thu Feb 07, 2013 7:15 am

My vote goes to multiple values, struct, or object unless list makes extremly more sense :)
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

garethw
Posts: 43
Joined: Fri Jul 13, 2012 12:56 pm
Location: Ottawa, ON

Re: Returning multiple values vs returning a list of data

Post by garethw » Wed Feb 13, 2013 10:30 pm

sylwester wrote:My vote goes to multiple values, struct, or object unless list makes extremly more sense :)
I take your point, but it sidesteps my question. It wasn't whether a list is a great representation of structured data; it was when to use any composite data structure - list, struct, object, anything - versus multiple values. It is a deeper question than mere selection of an appropriate single type.
~garethw

pjstirling
Posts: 166
Joined: Sun Nov 28, 2010 4:21 pm

Re: Returning multiple values vs returning a list of data

Post by pjstirling » Thu Feb 14, 2013 11:58 am

Your reply has saved me from worrying about necro'ing this thread! :D

Always return multiple values unless your function's clients (if it has no clients why does it exist?) would always want to create a composite type to hang onto them.

You said that you found dealing with multiple values 'awkward' but didn't make it explicit as to which aspect of MULTIPLE-VALUE-BIND you didn't like. I see the following things that you might dislike:
  • length of its name
  • length of its name leading to long lines (especially once you have to bind a lot of different values)
  • increased indenting from interleaved LET, LET*, MULTIPLE-VALUE-BIND, DESTRUCTURING-BIND, WITH-SLOTS, etc when binding temporary variables
The first merely requires getting tab-completion activated in your editor of choice. I'm not an 'only emacs will do' zealot, but I haven't gotten further than doing 1-line edits in vim, so I can't help you with that.

The second can be a grey area, I personally still wouldn't return a composite type for less than 7-10 results (unless a composite type was called for).

The third isn't a reason not to use multiple values, it's a reason not to use MULTIPLE-VALUE-BIND (directly). A 5-minute macro to unify the syntax:

Code: Select all

(defmacro bind ((&rest bindings) &body body)
  (setf body `(locally ,@body))
  (let (let-bindings)
    (labels ((add-let-binding (binding)
               (push binding let-bindings))
             (emit-let-bindings ()
               (when let-bindings
                 (setf body
                       `(let* ,let-bindings
                          ,body))
                 (setf let-bindings nil))))
      (dolist (binding (reverse bindings))
        (if (listp binding)
            (case (first binding)
              (:mv
               (emit-let-bindings)
               (setf body
                     `(multiple-value-bind ,(second binding) ,(third binding)
                        ,body)))
              (:db
               (emit-let-bindings)
               (setf body
                     `(destructuring-bind ,(second binding) ,(third binding)
                        ,body)))
              (t
               (add-let-binding binding)))
            ;; else
            (add-let-binding binding)))
      (emit-let-bindings)))
  body)
Which you might use like:

Code: Select all

(bind (nil-variable
       (variable-with-initialiser 1)
       (:mv (quotient divisor) (floor 5 2))
       (:db (a b (c d) e) '(f g (h i) j)))
  (declare (ignore ...))
  ...)
This handles LET*, MULTIPLE-VALUE-BIND, and DESTRUCTURING-BIND, and I would hope you could easily see how to extend it to others. There's a number of things that you might want to customise about this macro, or you could find a library with a different macro (I know that there's at least one, but I can't remember where I saw it).

The important (and delightful) thing about lisps is that, unlike blubs, you are only as far away from a convenient solution as your ingenuity and imagination! :D

sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

Re: Returning multiple values vs returning a list of data

Post by sylwester » Fri Feb 15, 2013 6:42 pm

Thanks for DESTRUCTURING-BIND bit. It actually tipped me from multiple values to be somewhat indifferent towards it all. With DESTRUCTURING-BIND and MULTIPLE-VALUE-LIST returning values and list is pretty much two ways to do the same and the difference is performance. However you made it clear performance wasn't a key issue (as it should't be in most cases). So when indifferent should one use multiple values because it performs better=
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

garethw
Posts: 43
Joined: Fri Jul 13, 2012 12:56 pm
Location: Ottawa, ON

Re: Returning multiple values vs returning a list of data

Post by garethw » Sat Feb 16, 2013 9:48 pm

Thanks, pjstirling - pure gold.

I'm still overcoming the mental barrier to syntactic abstraction. I'm getting reasonably competent at writing macros, but knowing what macros to write still doesn't come naturally to me.
~garethw

Post Reply