I picked that habit up from some of the examples in the forum, and it works for me. Is there a reason why one would really want multiple return values?
The one nice thing about multiple values that I don't think returning lists accomplishes is that if you have a function that doesn't expect to receive multiple values, it will just use the first value returned. For example, in CL, #'truncate returns two values, the quotient and the remainder. But if you pass the return values of #'truncate to #'+, it just pretends you only passed a single value.
* (truncate 5 3)
1
2
* (+ (truncate 5 3) 6)
7
I don't know of any way to make this work implicitly with returning lists... you would need to explicitly test if you were receiving a list and then destructure it accordingly. (Please correct me if you know of a better way around this.)
Anyway, I suppose that the programmer knows what the output is of a certain function, and not be surprised if a function churns out a list rather than two values ;).
What if your function originally just returned one value, but at some later point you realize that a second value would be useful in some situations?
With multiple return values you can just extend it without breaking existing clients. If, on the other hand, you add a list wrapper around the returned values, all call sites must be changed to take car of the list.
That would be useful indeed. The flip side of the coin might be something that was sort of mentioned in 'on lisp'. If all functions return only one value (be it a list or a single value) by default, you can write a general memoize layer around functions that doesn't have to check how many multiple return values are returned.
I also noticed a carif function in arc. If you are worried about single values that will become lists in the future, you might start using carif in your current clients.
Returning multiple values puts multiple non-GC'd values on the stack. This is fast, but capturing and using those values is somewhat inconvenient for the programmer. Returning a cons creates one other cons in the heap that will need to be GC'd for each additional value returned.
In theory we could do this with arc2c. However, I'm not 100% sure this is necessary with a "really good" optimizing compiler.
We could defer destructuring of arguments in arc2c to as late as possible, so that we could do some amount of optimizing away 'cons cells when the cells themselves are used only for returning multiple values. Which is arguably difficult, since each stage in arc2c expects arguments to be in undestructured form, i.e. (let (foo bar) niaw ...) => (let tmp niaw (with (foo (car tmp) bar (cadr tmp)))). If arguments are kept in non-destructured form, we would need to modify the way that function parameters are stored (to handle (o foo (expression))) and check each stage in the compiler.
Edit: When optimizing raymyers' treeparse, I actually transformed parsers to CPS form in order to return multiple values without all the construction and deconstruction of 'cons cells, which helped reduce some of the overhead.