Difference between multiple values ​​and regular 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 downside if quotient/remainder returns (cons _ _) rather than (values _ _) ?

+5
source share
4 answers

In 2002, George Caswell asked this question in comp.lang.scheme. The sequential thread is long, but has many ideas. The discussion shows that opinions are divided.

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

My answer then:

 > What are the motivations behind Scheme 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 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 
+7
source

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

values connected to call-with-values , and special forms like let-values are just syntactic sugar with this procedure call. The user must know the form of the result in order to use call-with-values in order to use the result. Returns are often made on the stack, and the call is also on the stack. The only reason for supporting the values in the Scheme would be that there is no overhead between the return of the manufacturer and the consumer call.

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

In Common Lisp, this is completely different. You can always use values ​​if you have additional information, and the user can still use it as a normal procedure if it only wants to use the first value. Thus, for CL, you will not need to supply quotient as an option, since quotient/remainder will work just as well. Only when you use special forms or procedures that take multiple values, the fact that the procedure returns more values ​​works the same way as with the Schema. This makes values better choice in the CL than Scheme, since you avoid writing one instead of several procedures.

In CL, you can access the hash as follows:

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

If you are not using a second return value, you do not know if T was the default value or the actual value. Here you see the second value indicating that the key was not found in the hash. Often you do not use this value, if you know that there are only numbers, the default value will already be a sign that the key is not found. In Racket:

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

The racket failure-result may be thunk, so you pass by, but I'm sure it will return several values, and if the values ​​work, as in CL. I guess that a more household with a CL version and schema, being a minimalistic language, might not want to give developers extra work.

+4
source

Edit: Alexei's missing comment on the same topic before posting this

One of the missed practical advantages of using multiple return values ​​over lists is that Racket 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 created by compose will pass the two values ​​returned by hello-goodbye , as two arguments to string-append . If you write code in a functional style with a lot of compositions, this is very convenient, and it is much more natural than directly moving values ​​around you using call-with-values , etc.

+4
source

It is also related to your programming style. If you use values , this usually means that you want to explicitly return n values. Using cons , list or vector usually means that you want to return a single value that contains something.

There are always pros and cons. For values : it can use less memory for some functions. The caller needs to use let-values or another syntax with multiple values. (I would like to use only let as CL.)

For cons or other types: you can use let or lambda to get the return value. You must explicitly deconstruct it to get the desired value using car or other procedures.

What to use and when? Again, depending on your programming style and in each case, but if the return value cannot be represented in one object (for example, private and the rest), then it is better to use values to make the procedure more understandable. If the return value is a single object (for example, name and age for a person), then it is better to use cons or another constructor (for example, a record).

+3
source

All Articles