Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between multiple values and plain tuples in Racket?

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 _ _)?

like image 533
Alex Avatar asked Apr 09 '15 05:04

Alex


4 Answers

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
like image 108
soegaard Avatar answered Oct 31 '22 15:10

soegaard


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.

like image 32
Sylwester Avatar answered Oct 31 '22 15:10

Sylwester


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.

like image 4
Jack Avatar answered Oct 31 '22 13:10

Jack


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).

like image 3
Takashi Kato Avatar answered Oct 31 '22 13:10

Takashi Kato