Returning multiple values vs returning a list of data
Returning multiple values vs returning a list of data
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?
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
Re: Returning multiple values vs returning a list of data
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.
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.
Re: Returning multiple values vs returning a list of data
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:
While this would not be quite the same
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.
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)
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)
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p
Currently I'm planning a Scheme compiler :p
Re: Returning multiple values vs returning a list of data
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.
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
Re: Returning multiple values vs returning a list of data
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
Currently I'm planning a Scheme compiler :p
Re: Returning multiple values vs returning a list of data
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.sylwester wrote:My vote goes to multiple values, struct, or object unless list makes extremly more sense
~garethw
-
- Posts: 166
- Joined: Sun Nov 28, 2010 4:21 pm
Re: Returning multiple values vs returning a list of data
Your reply has saved me from worrying about necro'ing this thread!
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:
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:
Which you might use like:
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!
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 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)
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 ...))
...)
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!
Re: Returning multiple values vs returning a list of data
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
Currently I'm planning a Scheme compiler :p
Re: Returning multiple values vs returning a list of data
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.
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