34

Normally I glance over this fact and just accept it as "this is how the wheel spins" but today I wonder where this comes from: Why does a function only have one return (reference)value?

Why can't, would it be hard, or not wise for a function to return multiple values? Is it because of objects and how you should expect that a package of data should always be returned in the form of a reference to an object?

If you see a declared function as a contract which states how a function can be called, which parameters it should take, and which return value type it has, then I don't see the logic behind having only one return value since you could apply the same logic the other way around (thus creating a contract for more than one return value).

The only reason based on logic I can see is that if a function returns more than 1 thing, it should also manipulate more than 1 thing for more than 1 reason which is against the philosophy that a function should do only one thing. If that one thing is manipulating an object and returning an other object it would make sense and you can return a reference value with the manipulated object..

So why exactly does this limitation exist?

Joop
  • 3,706
  • 34
  • 55
  • 9
    _"Why can't/ would it be hard/ not wise for a function to return multiple values?"_ You actually can, using an appropriate data structure or container? – πάντα ῥεῖ Apr 05 '15 at 12:35
  • With appropiate data structure you mean objects and with container you mean arrays (= also an object)? I am trying to find out why you can't return multiple values. I know you can use 1 reference value which can contain a lot of other values. – Joop Apr 05 '15 at 12:38
  • 1
    Because functions return values of a certain type. –  Apr 05 '15 at 12:38
  • 8
    The behaviour you cite is inherited from C, which in turn inherited it from ALGOL, etc. You're really asking about historical precedent. – Oliver Charlesworth Apr 05 '15 at 12:38
  • @RawN But the question is *why*? Why can't a function return, say, an `int`, a `double` and a `char`? – juanchopanza Apr 05 '15 at 12:39
  • @juanchopanza Because that's what they do by design. –  Apr 05 '15 at 12:40
  • 7
    @RawN The question is why it was designed this way. Jeeez. – juanchopanza Apr 05 '15 at 12:41
  • @OliverCharlesworth if that's true. Then why was it never added? I mean if the only reason is because of a historical reason, then I think it would be possible to add this functionality without breaking the previous code. – Joop Apr 05 '15 at 12:42
  • @juanchopanza In that case that's a too broad question and as such is not a good fit for this website. No need for the 'Jeeez' part. –  Apr 05 '15 at 12:42
  • 5
    @RawN It isn't too broad because this could be well documented and known. The jeez is because you're missing the obvious. – juanchopanza Apr 05 '15 at 12:43
  • The answer can be by design, for historical reasons, returns values of certain types. In my view that's a too broad question. –  Apr 05 '15 at 12:59
  • 1
    At least in C++, your functions can return a set containing several values (tuples). This is equivalent to returning several values. The rest is just syntactic sugar (and *yes*, syntactic sugar that may break backward compatibility if it is changed). – dureuill Apr 05 '15 at 12:59
  • 4
    Also, related: http://programmers.stackexchange.com/questions/203471/why-do-most-programming-languages-only-support-returning-a-single-value-from-a-f – dureuill Apr 05 '15 at 13:12
  • 1
    @OliverCharlesworth You should turn that comment into an answer. You got my upvote. –  Apr 05 '15 at 13:18
  • possible duplicate of [Return multiple values from a Java method: why no n-tuple objects?](http://stackoverflow.com/questions/7470861/return-multiple-values-from-a-java-method-why-no-n-tuple-objects) – RaGe Apr 30 '15 at 14:46
  • @RaGe: Not a duplicate; this question deals more with the historical precedent as opposed to specifically being about Java or C++. – Makoto Apr 30 '15 at 14:53

10 Answers10

35

Whenever you have multiple values x1 ... xN, you trivially have the tuple (x1, ..., xN) which is a single (albeit composite) value. If you don't have tuples in your language, use any aggregate type (struct, class, arrays, other collections) at will. So from this perspective, returning "multiple" values and returning a single value is completely equivalent.

That just means that you only need one, but why omit the other?

First, you need aggregate types anyway, so the choice is between having both or only having aggregate types. Second, if a function can return "multiple values", you face a semantic conundrum: Suddenly an expression does not evaluate to value (which we have previously defined very specifically), it's this new, different category of thing called "multiple values". Now what is the static type of this result? What can the program do and not do with it? What does it mean in implementation terms?

You can of course artificially answer these questions, but any sensible approach will just amount to tuple types. Ignoring that makes you blind to a very useful perspective, and refusing to make them first-class values is probably more complicated than saying "these are tuple types, they can be constructed like this and deconstructed like this".

  • 3
    LISP defines a simple rule to resolve your conundrum: the first value is used as *the* value of the expression; to access further values you need an explicit assignment idiom. This works nicely for many cases where there's a clear hierarchy of relevance of the returned values. The extra values returned, however, may be key to avoiding the repetition of expensive work. Canonical example: DIV returning (quotient,remainder) and MOD returning (remainder,quotient). When you need both, you don't want to execute the expensive division algorithm twice. – Marko Topolnik Apr 05 '15 at 17:55
  • @MarkoTopolnik It's a simple rule, but it feels weird to me. It may fit with the minimalistic Lisp approach (but then again, just retuning fixed-length lists would be even simpler). Oh, and now that you mention it: Lua does the same thing as Lisp though. Go also has multiple return values that aren't tuples, but Go refuses to drop the rest silently: One has to write `quotient, _ := div(a, b)`. –  Apr 05 '15 at 18:13
  • Lua's way seems to be the same as in LISP. You can notice the convenience of these rules compared to just returning a tuple. In fact, the rules are incompatible with the concept of returning a tuple so it actually makes sense to have *both* things in the language. – Marko Topolnik Apr 05 '15 at 18:49
  • @MarkoTopolnik They're incompatible alright, but having both is just redundant. If you have tuples, multiple return values can be trivially "emulated" by returning and destructuring tuples, padding with extra dummy bindings to ignore additional return values. Not nearly enough value for an extra feature and the potential for confusion and the headaches from the two probably having very similar syntax. –  Apr 05 '15 at 19:17
  • Padding with extra dummy bindings is not exactly a seamless emulation. You can't just nest a tuple-returning expression inside a larger expression and have it automatically adapted to the context such that the first tuple member is used. This is the essential difference between the two features and it's orthogonal to the low-level mechanics (internally it could be a reference-typed tuple or true multiple returns on the stack). – Marko Topolnik Apr 05 '15 at 19:43
  • How does this reasoning *not* also apply to arguments? Surely functions only need to accept one argument, which can be a tuple. – user253751 Apr 06 '15 at 09:22
  • 1
    @immibis The main argument is the "semantic conundrum", which does not apply at all to arguments. When multiple arguments are supplied equally many parameters are declared at the function definition, so each value is bound to the parameter in the corresponding position. Voilà, no multiple values where there would otherwise be one. –  Apr 06 '15 at 10:00
  • @immibis I agree, the same argument can symmetrically apply to arguments. If expressions were defined to never accept more than one argument, but produce any number of results, another consistent grammar for composing them would arise. It just turns out that many-to-one mappings have more use cases than one-to-many OTOH, a many-to-many mapping is a nightmare to compose and isn't something you want to have in your language. So the practical approach was to discard extra return values when the function is used as an argument to another function. – Marko Topolnik Apr 06 '15 at 14:12
7

This is purely historical. This is how usually mathematical functions work. Moreover, some languages (quite a lot actually) allow to return more than one value - usually using a tuple object.

The idea that it would be nice to return more than one object came late. Why was it chosen to be done via objects, and not language syntax? Because OO programming was in wild use and generally accepted.

Semantically both ways have the same meaning - "return more than one thing". So adding such functionality would be an equivalent to supporting another syntax for the same meaning. Having two ways to do one thing is not very welcomed in programming. Programmers wonder which way to chose, some only know one method, and compiler developers would have to maintain two mechanisms that do not differ in their functionality. It's a pretty good rationale. Okham's razor is appreciated tool. And objects are more useful.

Another thing is that, your argument: "a declared function as a contract which states how a function can be called, which parameters it should take, and which return value type it has", is not really good at the bold part.

Return type is often not considered a part of function signature. You can't have overloads that differ only in return value. That being said, of course compiler needs to know how many bytes it should take of the stack, after function finished, as the return value. So it's not totally irrelevant, though usually languages do not consider this.

luk32
  • 15,812
  • 38
  • 62
  • 1
    I elaborated a little more on it, because it is not totally irrelevant on the binary level. Though languages are often designed like that. – luk32 Apr 05 '15 at 12:52
7

When a function returns multiple things, the things are related to each other, at the very least by virtue of being returned by a single function. This consideration alone is sufficient to require the authors of functions that return multiple things to group these multiple things together by creating an appropriate data structure, which is already possible with functions returning a single item.

For example, in C++ std::set::insert needs to return two values - an iterator, and a bool to indicate whether the iterator is some old element or the element that we have just inserted. The solution was to return an std::pair:

pair<iterator,bool> insert (const value_type& val);

The second consideration is purely practical: dealing with multiple returned value in expressions like that

int x = f(y);

is simple and intuitive when there is only one return value. Trying to return multiple values, as in

int x, y = f(w, z);

would become hard to parse, because the reader must check the return type of f and see if it returns an int or two ints.

I am not even touching the situation when the data types of the returned items are not the same! The syntax would quickly become a nightmare, without a comparable gain in expressive power of the language.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 6
    The second is a non-issue if the syntactic ambiguity is removed, by writing "unpack multiple return types" differently (say, `(int x, int y) = f(w, z)`) or getting rid of the `int x, y;` syntax for declaring multiple variables. –  Apr 05 '15 at 12:47
  • 3
    @delnan There's definitely a way to make the syntax "work". The price of making it work, however, may be too high. – Sergey Kalinichenko Apr 05 '15 at 12:49
  • Then make *that* your argument, but be prepared to defend it. I don't think your straw man syntax (not meant derogatory) works anyway, since there is no place to specify the syntax of `y` if `f` returns two values of different type. –  Apr 05 '15 at 12:51
  • "[...] specify the **type** of `y` [...]", that is. –  Apr 05 '15 at 12:58
  • 3
    The first paragraph of your reply makes me wonder. If one can say that, one can say just as well: *When a function consumes multiple things, the things are related to each other, at the very least by virtue of being consumed by a single function. This consideration alone is sufficient to require the authors of functions that consume multiple things to group these multiple things together by creating an appropriate data structure.* – ach Apr 05 '15 at 16:37
  • 2
    Your example with `insert` returning a `pair` (essentially a 2-tuple) only shows the weakness of your argument, because tuples are basiically used for ad-hoc grouping of semantically unrelated things. Thus it's nothing more but a trick to overcome a syntax limitation. – ach Apr 05 '15 at 16:42
  • 1
    @AndreyChernyakhovskiy That tuples *can* be used for ad-hoc grouping of semantically unrelated things, does not mean that it is the *only* way in which they can be used. As for reversing my first argument goes, it's true more often than it may appear: I've seen lots of functions that take many arguments simply because their authors *can* freely pass many arguments, when some or all of the arguments are very strongly related to each other. – Sergey Kalinichenko Apr 05 '15 at 18:45
6

Your question doesn't make it clear which exactly feature you're looking for: syntactic sugar which makes it "look" like you're returning several values, or the essential ability of a function to return several values.

If it is the former, then there's no big answer to give: languages just differ in the level of syntactical support they provide for this. For example, LISP has full support for multiple value return and many languages (Scala, Clojure) support the destructuring bind idiom which quite closely resembles what you're looking for, but work against reference-typed tuples. Java happens to be poor on syntax in general so its lack of support for the destructuring bind is no surprise.

As for the latter — in C, returning a struct type is exactly the functionality you are looking for. The struct will be placed on the stack frame and will contain an arbitrary number of values. Other languages, which don't let you control stack/heap placement at such a level of detail, still allow you to return a pointer to such a struct.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 1
    Python also has nice syntactic sugar for tuples: (a, b) is a tuple, and the parentheses can often be forgotten when they are not strictly required, e.g. `return a, b` is a valid statement. Also thanks for clarifying between syntactic sugar and actual feature. – dureuill Apr 05 '15 at 13:05
  • @dureuill Of course, in C we already have the comma operator which does something very different, and is sometimes used to good effect. – user Apr 05 '15 at 20:13
6

I wish people stopped interpreting “I can't do X in language Y” as “language Y has a limitation that prevents me from doing X”. Sometimes this is in fact just true – for instance C++' const modifiers basically just forbid you from mutating values in a way that, syntax-wise, could be expressed perfectly well, but just isn't deemed wise if you want to ensure your program behaves in a certain predictable way.

OTOH, for most features X you could come up with it just doesn't make any sense to want them in a language. How about if I asked, “what is the reason I can't just differentiate all C++ functions?” For a physicist or engineer who knows nothing about programming this wouldn't seem a far-fetched thing to want, as these guys traditionally only deal with1 a particular kind of functions in the maths sense, which can always be differentiated as often as you like.

But for most functions in programming languages, it absolutely doesn't make any sense, conceptually, to take the derivative. To a programmer, the analytic functions can be but a tiny subset of all functions, simply because the idea of derivatives only applies to continuous domains (in practice, basically to vector spaces)2, and of course it is completely incompatible with the side-effects that “functions” in procedural and OO languages are allowed to have.

Now, allowing to return multiple values sure appears a much more mundane thing than allowing to differentiate functions, but it still changes in quite a fundamental way what's meant by “function”. You can't just refer to the return type of a function anymore, you need to clarify what it means to compose functions with multiple return values, you need to come up with some new syntax for function pointers, etc. etc.
Unlike the differentiation example, all of this this would probably be possible, but it should be clear that it would change pretty much all of the language. It would very much not be just “stop preventing me from returning multiple things!”

Some languages, e.g. Fortran and Matlab, have hard-wired support for multiple return values into the language. Oh boy, I can tell you this does not make programming in these languages nicer: many things you can easily do with C++ functions are just blocked because they would get messed up by the multiple returns!
Other languages, e.g. Python, have a lightwight alternative: it looks like you're returning multiple things, but really you're just returning a single tuple and get some syntactic sugar for pattern-matching the components back into individual variables.

Returning tuples or something similar is of course, as said by the other answers, the sensible solution to your problem, though it can sometimes get a bit clumsy in languages that don't have very good syntactic support for tuples. (But that's just a bit of harmless boilerplate.)


1Well, at least they pretend they're dealing with analytic functions. Often, this is barely acceptable mathematically. Really, they're just approximating everything by analytic functions.

2Interestingly, in a language with proper algebraic data types, a remarkable analogue to the classical idea of differentiation turns up in quite a surprising way, without actually needing continuous domains. That's still something quite different from e.g. “take the derivative of x2 / (1+ex)”.


Going further: it can be argued that functions should also have only a single argument. That, too, can be a composite type, but you don't even need tuples here: there's a thing called Currying – any function of two arguments can also be expressed as a function of a single argument, returning a function of the second argument. This may seem a bit of a strange way of thinking, but actually it works out quite nicely. Haskell does this consequently, and many people consider it one of the crazy things about the language, but it allows you to express some operations more consisely than possible in most other languages, while at the same time the syntax is actually kept very simple.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • 1
    Great perspective and thinking. Thank you for this perspective, I especially found the reason the other way around with Haskell very fun to see. I had atleast one question, maybe this is hard to come up with on the spot, but can you also give an example of when C++ is easier than Matlab because returning multiple values messes up things for Matlab? I use matlab quite often but can't come up with an actual example myself. – Joop Apr 05 '15 at 19:54
  • @Joop: Well, to give a concrete example of how specifically Matlab's returns are evil, I once was given a task to do something in Matlab using a given helper function that was supposed to return three things. After I'd done most of the work, I noticed that, in a special case, the function would actually return only _two_ instead of three values – and that really screwed up my code! In a language like C++, it would have had to be made very explicit what values are returned, and which might be optional. (Of course, it that case the code would have just needed better documentation...) – leftaroundabout Apr 06 '15 at 22:39
  • A bigger problem with those multiple returns is that it makes _abstracting_ over functions a complete nightmare. Want to generically compose two functions? Ok, now how many values does the first one return, how many of these should the second obtain and in which order? When there is just one return with properly-specified (but possibly itself generic!) compound type, then you can simply abstract also over that generic intermediate type, possibly insert a polymorphic handler to extract those return components you want, and be sure it'll _always_ work, not just for some specific test function. – leftaroundabout Apr 06 '15 at 22:47
5

Back in the day certain CPU registers had particular conventions, and CPU registers were limited. So assembly code typically returned exactly one value, for example, in AX on the 8086.

Writing assembly code is laborious, and 'C' came along to relieve some of the burden while still allowing the developer to achieve similar performance of assembly code.

To emphasize the point, consider this 'C' code:

int foo(void)
{
    // no return statement; default register used
}

In this case the function foo will return the value in the "return value" register for the CPU type, for example, EAX on a 32-bit x86 processor.

'C++' is inter-operable with 'C' and does not introduce a second form of the return statement.

jws
  • 2,171
  • 19
  • 30
4

I have never worked on compiler internals before, but the term function generally refers to a binary relation, that has a unique mapping for every item in it's domain(set of inputs) to it's codomain(set of outputs). Now, those set elements could themselves be aggregate. Maybe someone who is more well versed with compiler theory could elaborate on this.

basav
  • 1,475
  • 12
  • 20
  • 2
    Well, that's a math POV, not necessarily qualifying a programming language design decision. – πάντα ῥεῖ Apr 05 '15 at 12:49
  • @πάνταῥεῖ: the fact that most programming languages rather blatantly disrespect maths arguments in their design doesn't mean these arguments aren't good, even if retconned to a language specification. – leftaroundabout Apr 05 '15 at 16:40
  • You'd love FORTRAN then? – πάντα ῥεῖ Apr 05 '15 at 16:48
  • @πάνταῥεῖ Fortran? I actually work a lot with Fortran, and don't particularly like it... but mainly because it's just ancient and carries a lot of historical garbage in its syntax, and because most of the legacy code is written by non-programmers. — Honestly, Fortran is just as un-mathematical as most imperative languages. It's often thought to be a “mathematical” domain-specific language, but really it's rather a DSL for particular problems in science and engineering – number crunching which happens to use a lot of maths, but hardly very “deep” maths. – leftaroundabout Apr 05 '15 at 18:31
4

In Mathematics, a function has a domain and a codomain. f:S->T denotes a function with domain S and codomain T.

A method with two parameters and one return value more or less corresponds to a function f:SxT->U, where x denotes the Cartesian product. So, for example, a method with parameters of type double and int that returns a boolean corresponds roughly to a function f:DxI->B where D is the set of all double values etc. This is done without bundling the two parameters together into a tuple.

There is no reason why we couldn't similarly have a method corresponding to a function of the form f:X->YxZ without the two returned values being bundled together into a tuple.

For example, the Java method

BigInteger[] divideAndRemainder(BigInteger divisor)

could easily have the signature

BigInteger q, BigInteger r divideAndRemainder(BigInteger divisor)

Somewhere in the body of the method there would be a return statement that could look like return q, r;. There would be no reason for the array at all.

In fact this would be better as you could call the method by writing

BigInteger q, BigInteger r = bigInt1.divideAndRemainder(bigInt2);

instead of

BigInteger[] arr = bigInt1.divideAndRemainder(bigInt2);
BigInteger q = arr[0];
BigInteger r = arr[1];

I guess the reason this is not allowed is that it would complicate the language without there being a great demand for it. If you look in standard libraries in many languages and compare the number of methods that bundle many return values into a single object, compared to the number of methods that have more than one argument, you would probably find that the latter is much more common.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • Note that LISP does have exactly the thing you have described. Not many other languages followed suit, though, and my best guess at a reason is that it doesn't really buy much. There is no essential distinction between a tuple and a list of separate values, it's just formalism. – Marko Topolnik Apr 05 '15 at 13:18
  • @MarkoTopolnik The distinction I am making between a tuple and a pair of values is that when I say tuple I really mean an array or some other kind of container object. What I am trying to argue is that this is possible to do without the need for a container object to be created, Do you know how LISP does it? Is it just syntactic sugar for an array being created? – Paul Boddington Apr 05 '15 at 13:23
  • 1
    LISP actually allows you to place several return values on the stack. But in the end, that's exactly the same as placing a `struct` on the stack. So LISP's approach is like a combination of a stack-based `struct` and destructuring bind syntax. – Marko Topolnik Apr 05 '15 at 13:54
3

Functions are just subroutines that leave something in the stack. If what is left in the stack isn't predictable you now have to have some method predicting what all is left on the stack. That creates overhead. We decided to just point at one thing that might be a data structure of many things or might just be an int.

A mathematician will tell you a function is about relating each argument (input value) to the corresponding output value but to a computer scientist a function is contract about the state the stack will be left in when it returns.

candied_orange
  • 7,036
  • 2
  • 28
  • 62
  • Different contracts could be chosen. Allowing multiple return values does not mean that you can't compute the size and number of return values. – dureuill Apr 05 '15 at 12:52
  • This is true. But then you could hardly call them functions. Those would be relations. Functions turned out to be more popular. – candied_orange Apr 05 '15 at 12:53
  • 1
    Keep your mathematical terminology out of my programmer jargon. A function is a subroutine. –  Apr 05 '15 at 13:00
  • 1
    No, one can build a function from A to AxA, that will still be a function as long as x is mapped to a single f(x) (even if f(x) lives in AxA). It will cease to be a function if a single x can be mapped to several, distinct, f(x). Which is not what OP is asking about. – dureuill Apr 05 '15 at 13:01
  • Of course you can. But it comes with a cost. If you want to leave an unpredictable amount of data on the stack you have to create overhead that reports that amount of data. – candied_orange Apr 05 '15 at 13:10
  • 2
    Who says unpredictable? Returning multiple values doesn't mean that you don't know how many values your function returns at compile time. The syntactic sugar of returning several values could even be processed at compile time as returning a struct containing the various returned types. – dureuill Apr 05 '15 at 13:16
  • From the perspective of the stack returning a struct is identical to returning a object. In either case only one address is left on the stack to be popped. Returning multiple values would mean they are not packed in a stuct or object on the heap but each individually on the stack. That means there must be some way to know how many there are even if it's simply to become part of the functions signature. It's not that different a problem than parameters being pushed before the call. But you don't get it solved for free. – candied_orange Apr 25 '15 at 20:04
1

I recognize the problem. Let's say you cycle through a collection of data and you need to filter the data on some aspect. As a side effect of that traversing data you will find out another interesting feature that you would like to have as well, but that result is not inline with the requested outcome of the function.

An example: you traverse all your code to find out which functions are called by which functions. While traversing you might find as a side effect functions that are never used/called upon. You will only find those functions when traversing all functions, it is not a result of the call, it is strictly spoken not related to the outcome, yet it is very interesting information to retrieve. What should you do? Write another function performing the same functionality, but now with a different outcome? Creating a separate object storing both results? Store the outcome in a new variable to the object? Or have a function with multiple outcomes?

Those side effects are unpredictable from the perspective of designing a programming language. You really can't design a language on unpredictable requirements. And how should the next functions respond to it? In order to keep functions as loosely coupled from each other as possible, is it a good practice to keep the outcome of functions as simple as possible. Function design should apply the Law of Demeter. Letting functions have an output of unrelated results makes it impossible to write loosely coupled code. It can become a real nightmare if you have to change a chain of functions, because the first function with multiple output drops one of the results - or adds a new one to it.

My solution to the forementioned problem was to 'write' the code twice. One time to find all relations between functions and the other one to find all functions that were never called upon. This way there is a clean separation between content of the function and the rest of the application. I just pretended that I did not know that I wrote that function before.

I think that it is a good design to let a function only produce one coherent result. It makes the design of the whole process a lot more structured, which helps to build bigger and more complex applications.

Loek Bergman
  • 2,192
  • 20
  • 18