Difference between multiple values and plain tuples in Racket?
Asked Answered
L

4

5

What is the difference between values and list or cons in Racket or Scheme? When is it better to use one over the other? For example, what would be the disadvantage if quotient/remainder returns (cons _ _) rather than (values _ _)?

Laurinda answered 9/4, 2015 at 5:3 Comment(3)
The most objective answer is that values is probably marginally faster because it performs less allocation. Functions that return multiple values can also be composed (with compose) in interesting ways, but I've rarely found that useful. Otherwise, I think it's probably pretty subjective, but I'm of the camp that values should probably usually be avoided.Valuation
Jay McCarthy has a great blog post about symmetry of function calls and returns. Functions can be called with zero or more values -- and also return zero or more values. In fact, "return" is a kind of "call": "When a function normally returns, you can think of it as 'calling its continuation' with the return value. All values does it call that continuation with more arguments". He goes on to show how to make it fully symmetrical including optional and keyword return values.Caracara
@GregHendershott Yes, call-with-values is the ultimate plug-in-your-own-continuation system. I've actually used case-lambda with call-with-values on occasion, though I have not tried using keyword arguments! I think my reason for not trying the latter is that I don't believe values supports that: you'd have to directly call the continuation to use keywords.Selfexistent
O
7

Back in 2002 George Caswell asked that question in comp.lang.scheme. The ensuing thread is long, but has many insights. The discussion reveals that opinions are divided.

https://groups.google.com/d/msg/comp.lang.scheme/ruhDvI9utVc/786ztruIUNYJ

My answer back then:

>    What are the motivations behind Scheme's multiple return values feature?
> Is it meant to reflect the difference in intent, or is there a
> runtime-practical reason?

I imagine the reason being this.

Let's say that need f is called by g. g needs several values from f.
Without multiple value return, f packs the values in a list (or vector),
which is passed to g. g then immediately unpacks the list.

With multple values, the values are just pushed on the stack. Thus no
packing and unpacking is done.

Whether this should be called an optimization hack or not, is up to you.

--
Jens Axel Søgaard

We don't need no side-effecting         We don't need no allocation
We don't need no flow control           We don't need no special-nodes
No global variables for execution       No dark bit-flipping for debugging
Hey! did you leave the args alone?      Hey! did you leave those bits alone?
(Chorus)                  -- "Another Glitch in the Call", a la Pink Floyd
Orientalism answered 9/4, 2015 at 16:50 Comment(0)
S
4

They are semantically the same in Scheme and Racket. In both you need to know how the return looks like to use it.

values is connected to call-with-values and special forms like let-values are just syntax sugar with this procedure call. The user needs to know the form of the result to use call-with-values to make use of the result. A return is often done on a stack and a call is also on a stack. The only reason to favor values in Scheme would be that there are no overhead between the producer return and the consumer call.

With cons (or list) the user needs to know how the data structure of the return looks like. As with values you can use apply instead of call-with-values to do the same thing. As a replacement for let-values (and more) it's easy to make a destructuring-bind macro.

In Common Lisp it's quite different. You can use values always if you have more information to give and the user can still use it as a normal procedure if she only wants to use the first value. Thus for CL you wouldn't need to supply quotient as a variant since quotient/remainder would work just as well. Only when you use special forms or procedures that take multiple values will the fact that the procedure does return more values work the same way as with Scheme. This makes values a better choice in CL than Scheme since you get away with writing one instead of more procedures.

In CL you can access a hash like this:

(gethash 'key *hash* 't)
; ==> T; NIL

If you don't use the second value returned you don't know if T was the default value or the actual value found. Here you see the second value indicating the key was not found in the hash. Often you don't use that value if you know there are only numbers the default value would already be an indication that the key was not found. In Racket:

(hash-ref hash 'key #t)
; ==> #t

In racket failure-result can be a thunk so you get by, but I bet it would return multiple values instead if values did work like in CL. I assume there is more housekeeping with the CL version and Scheme, being a minimalistic language, perhaps didn't want to give the implementors the extra work.

Stoma answered 9/4, 2015 at 14:7 Comment(0)
G
4

Edit: Missed Alexis' comment on the same topic before posting this

One oft-overlooked practical advantage of using multiple return values over lists is that Racket's compose "just works" with functions that return multiple values:

(define (hello-goodbye name)
  (values (format "Hello ~a! " name)
          (format "Goodbye ~a." name)))

(define short-conversation (compose string-append hello-goodbye))

> (short-conversation "John")
"Hello John! Goodbye John."

The function produced by compose will pass the two values returned by hello-goodbye as two arguments to string-append. If you're writing code in a functional style with lots of compositions, this is very handy, and it's much more natural than explicitly passing values around yourself with call-with-values and the like.

Gaston answered 10/4, 2015 at 3:28 Comment(1)
compose makes more sense. Document says "to provide symmetry with the fact that a procedure can accept multiple arguments."Cilo
D
3

It's also related to your programming style. If you use values, then it usually means you want to explicitly return n values. Using cons, list or vector usually means you want to return one value which contains something.

There are always pros/cons. For values: It may use less memory on some implemenentations. The caller need to use let-values or other multiple values specific syntax. (I wish I could use just let like CL.)

For cons or other types: You can use let or lambda to receive the returning value. You need to explicitly deconstruct it to get the value you want using car or other procedures.

Which to use and when? Again depending on your programming style and case by case but if the returning value can't be represented in one object (e.g. quotient and remainder), then it might be better to use values to make the procedure's meaning clearer. If the returning value is one object (e.g. name and age for a person), then it might be better to use cons or other constructor (e.g. record).

Dianndianna answered 9/4, 2015 at 7:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.