8
$\begingroup$

I would like to ask a question about multiple return values, why this construct is not preferrable in programming languages (conceptual and/or technical difficulties). I've heard something about stack frames and how they reserve memory for return value and variable return values could make this problematic, but if someone could explain this better, this would be much appreciated.

In conctanetative languages (like FORTH), having multiple return values from a function is a common and very useful thing, and I imagine something like this in java/c-like languages would be useful too (a very basic example):

x, y = multRet(5);
multret(int x) {
    return x+1;
    return x+2;
    exit;
}

To clarify: I am not asking how to return multiple values (this is a known question with known answers), but I want to get a clarification about why this practice is not common in programming languages.

$\endgroup$
9
  • 4
    $\begingroup$ One possible explanation is that, if you need to return multiple values, you can always do that by returning a struct or list. Why complicate the langauge and compiler? $\endgroup$ Aug 22, 2014 at 11:29
  • 4
    $\begingroup$ I don't think the question is answerable. First, this is just a matter of taste. Second, I think the premise that it is "uncommon" is false. There are a huge number of languages which support returning tuples, and have been since the 1970s, and the practice of supporting returning tuples seems to be gaining popularity as all the recently designed popular system programming languages (Swift, Go, Rust) support returning tuples. $\endgroup$ Aug 22, 2014 at 11:44
  • 2
    $\begingroup$ I agree with @DavidRicherby; such a feature would be redundant and have potentially difficult/shady semantics (what happens with i=0; while (true) { return i; i+=1 }?). Note that some functional languages offer tuples, a better alternative. $\endgroup$
    – Raphael
    Aug 22, 2014 at 11:45
  • 1
    $\begingroup$ @WanderingLogic A tuple is still one value, returned by one return statement. When returning functions/lambdas, you may have a point. $\endgroup$
    – Raphael
    Aug 22, 2014 at 11:46
  • 1
    $\begingroup$ I disagree that this question is opinion-based: the comments so far indicate that there are technical answers. If you feel that the "why" part of the question is too subjective, it can easily be edited to "What are the advantages and disadvantages of allowing multiple return values?" $\endgroup$ Aug 22, 2014 at 12:08

4 Answers 4

6
$\begingroup$

Functions are traditionally thought of as returning a single value - in math. Of course several values form a tuple, but oftentimes in math we are dealing with real-valued functions. I suspect that this origin of functions is the reason why multiple return values are not so common. That said, it's easy to emulate them in several ways (by returning a tuple, or by having several output parameters, i.e. ones passed by reference), and some common modern languages (such as Python) natively support this construction.

$\endgroup$
6
$\begingroup$

Having multiple return values is preferable. Lacking them is bad design, plain and simple. However, we should clear up the misconception that such a thing even exists in the sense you are thinking of.

In languages based on the typed lambda-calculus (the progenitor programming languages), e.g. ML-family, Haskell, a function (lambda abstraction) only accepts a single argument and "returns" (more formally, evaluates to) a single result. So how can we create a function that seems to take or return multiple values?

The answer is that we can "glue" values together by using product types. If A and B are both types, then the product type $A \times B$ contains all tuples of the form $(a, b)$, where $a: A$ and $b: B$.

Thus, to create a function multiply that multiplies two integers together, we give it the type multiply: int * int -> int. It accepts a single argument which happens to be a tuple. Likewise, a function split that splits a list into two would have type split: a list -> a list * a list.

Maybe 30 years ago implementation was a valid concern in including such a feature. According to the C calling convention cedl (and it is just that, a convention - no god ordained it), the return value is stored in a special register, usually EAX. Certainly, this is extremely fast to read and write, so the idea has some merit.

But it is too limiting, as you point out, and there is no reason we need to follow this rule nowadays. For example, we could instead use the following policy: for a small number of return values, use a set of specified registers (we have more registers for use now than in the 80s). For larger data, we could return a pointer to a location in memory. I don't know what's best; I'm not a compiler writer. But the archaic C calling convention should have no influence on the semantics of modern languages.

$\endgroup$
1
$\begingroup$

Syntax is not necessary when simple patterns do the job better and your proposed syntax would be historically confusing to a lot of people. This means your syntax is hard to learn and will lead to problems when people switch between your language and others.

The simple pattern that solves this problem is defining a Variable named "returnValue" at the start of your method and then assign values to it. If you need multiple values, then simply use a collection of some sort and add the values instead. Then return this Variable. Problem solved.

Your syntax would mean that you would by default have to handle collections. This is frustrating for function that only return 1 value.

And what if someone does something wrong ? Eg.

x, y = multRet(5);
multret(int x) {
    return x+1;
    exit;
}

or

x = multRet(5);
multret(int x) {
    return x+1;
    return x+1;
    exit;
}

Is the compiler supposed to catch that? What if you have a loop?

At best this would be syntactic sugar. Something that doesn't add new features but instead only makes current patterns more comfortable and shorter in code.

At worst this would lead to horrible, horrible error in code. Null pointer exception in masses.

$\endgroup$
1
  • 1
    $\begingroup$ The compiler can catch multiple return values just fine. It's what the Go programming language does. $\endgroup$
    – slebetman
    Oct 14, 2016 at 21:38
1
$\begingroup$

It is mostly because of syntactical reasons; in your own example the first return statement would return one value and return, and never reach the second return statement.

There are nowadays a few languages that allow returning a tuple, that is zero, one or more values of arbitrary types, packaged together to be a single value. Still, they are returning one value, just a slightly more complicated value. Usually such languages would also have a way to assign a tuple to zero, one or more matching variables, often with an easy way to ignore some elements of the tuple. This could look like this:

Assign tuple to another tuple:

x = multRet(5); // x is a variable of type tuple (int, int)
multret(int x) {
    return (x+1, x+2);
}

(x, y) = multRet(5); // x, y are variables of type int
multret(int x) {
    return (x+1, x+2);
}

(x, _) = multRet(5); // x is a variable of type int, second component ignored. 
multret(int x) {
    return (x+1, x+2);
}

That syntax wouldn't be compatible with C, where (x, y) is a comma expression in parentheses. On the other hand, receiving two values is much easier than in C, where you either pass the two values using pointers, or you declare a struct for this purpose, fill it, and return it. Using pointers, the third example where the caller is only interested in one value is a pain, so this method of returning tuples is very useful.

$\endgroup$
5
  • $\begingroup$ Go is one example where the return values are separate values instead of a tuple. In Go you can do the second and third examples but the first example would result in compile-time error: the compiler expects two variables but gets only one. The syntax chosen by Go is simply return x, y and to accept the returning values you'd do x,y = multiRet() $\endgroup$
    – slebetman
    Oct 14, 2016 at 21:42
  • $\begingroup$ You mean Go has no variables of tuple type? $\endgroup$
    – gnasher729
    Oct 14, 2016 at 22:21
  • $\begingroup$ Like any other C-like languages Go has structs and like most modern languages Go has a map type (hash/associative array) so you can implement tuples either as structs or maps. One thing Go has that I don't see often is anonymous structs - structs without a name. $\endgroup$
    – slebetman
    Oct 15, 2016 at 7:44
  • $\begingroup$ Well, newer languages like Swift have tuples. Having to implement tuples yourself takes most of the advantage away. $\endgroup$
    – gnasher729
    Oct 15, 2016 at 16:23
  • $\begingroup$ In LISP its common. We can also use multiple-value-bind to assign each return value to a variable $\endgroup$ Jun 4, 2019 at 11:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.