133

Is there a reason why functions in most(?) programming languages are designed to support any number of input parameters but only one return value?

In most languages, it is possible to "work around" that limitation, e.g. by using out-parameters, returning pointers or by defining/returning structs/classes. But it seems strange that programming languages were not designed to support multiple return values in a more "natural" way.

Is there an explanation for this?

samthebrand
  • 368
  • 2
  • 12
  • 27
M4N
  • 1,447
  • 3
  • 12
  • 10
  • Not entirely relevant but the XC language (mostly based on C) used with [XMOS](http://www.xmos.com/) devices supports multiple return values. – rath Jul 02 '13 at 22:24
  • 41
    cause you can return an array... – nathan hayfield Jul 02 '13 at 22:55
  • 6
    So why not only allow one argument? I suspect it's tied to speech. Take some ideas, smoosh 'em into one thing that you return to whoever's fortunate/unfortunate enough to be listening. The return is almost like an opinion. "this" is what you do with "these." – Erik Reppen Jul 03 '13 at 02:48
  • 31
    I believe python's approach is quite simple and elegant: when you have to return multiple values simply return a tuple: `def f(): return (1,2,3)` and then you can use tuple-unpacking to "split" the tuple: `a,b,c = f() #a=1,b=2,c=3`. No need to create an array and manually extract elements, no need to define any new class. – Bakuriu Jul 03 '13 at 07:40
  • 7
    I can inform you that Matlab has a variable number of return values. The number of output arguments is determined by the calling signature (e.g. `[a, b] = f()` vs. `[a, b, c] = f()`) and obtained inside `f` by `nargout`. I'm not a big fan of Matlab but this actually comes in quite handy at times. – gerrit Jul 03 '13 at 10:06
  • 6
    I think if **most** programming languages are designed that way is debatable. In the history of programming languages, there were some very popular languages created that way (like Pascal, C, C++, Java, classic VB), but today there are also a hell lot of other languages getting more and more fans which allow multiple return values. – Doc Brown Jul 03 '13 at 16:34
  • @DocBrown: that's why I put the question mark. It is true for all the languages I'm familiar with. – M4N Jul 03 '13 at 20:31
  • 2
    @ErikReppen, it's actually a good idea to only allow one argument, as in lambda calculus - see `currying`. – SK-logic Dec 22 '13 at 08:35
  • 2
    I believe Haskell's approach is quite simple and elegant: when you have to return multiple values simply return a tuple: `f () = (1, 2, 3)` and then you can use pattern matching to "destruct" the tuple: `(a, b, c) = f () --a=1, b=2, c=3`. No need to create a list and no need to define any new datatype. – Thomas Eding Dec 23 '13 at 02:43
  • http://planetmath.org/node/30360 There's the definition of a function. It applies to computer science just the same. There's no reason the function can't map the input to a set. – James Sumners Dec 25 '13 at 15:29
  • 1
    I don't believe this question is too broad, and the answer is quite simple: Functions are required to return a single value because otherwise they could not occur in expressions. This would severely limit the use of the function an make the language much more clunky. Returning a single composite value like a tuple does not have this problem. Languages like Python which look like they support multiple return values just supports de-structuring of tuples into multiple variables, which happen at the call site. – JacquesB Dec 17 '16 at 10:09

14 Answers14

62

Some languages, like Python, support multiple return values natively, while some languages like C# support them via their base libraries.

But in general, even in languages that support them, multiple return values are not used often because they're sloppy:

  • Functions that return multiple values are hard to name clearly.
  • It's easy to mistake the order of the return values

    (password, username) = GetUsernameAndPassword()  
    

    (For this same reason, many people avoid having too many parameters to a function; some even take it as far as to say a function should never have two parameters of the same type!)

  • OOP languages already have a better alternative to multiple return-values: classes.
    They're more strongly-typed, they keep the return values grouped as one logical unit, and they keep the names of (properties of) the return values consistent across all uses.

The one place they are pretty convenient is in languages (like Python) where multiple return values from one function can be used as multiple input parameters to another. But, the use-cases where this is a better design than using a class are pretty slim.

  • 53
    Its hard to say that returning a tuple is returning multiple things. Its returning **one tuple**. The code you've written just unpacks it cleanly using some syntactic sugar. –  Jul 02 '13 at 23:27
  • 11
    @Lego: I don't see a distinction - a tuple is by definition multiple values. What would you consider "multiple return values," if not this? – BlueRaja - Danny Pflughoeft Jul 02 '13 at 23:42
  • 20
    Its a really hazy distinction, but consider an empty Tuple `()`. Is that one thing or zero things? Personally, I would say its one thing. I can assign `x = ()` just fine, just like I can assign `x = randomTuple()`. In the latter if the returned tuple is empty or not, I can still assign the one returned tuple to `x`. –  Jul 02 '13 at 23:46
  • 1
    I agree with @LegoStormtroopr - Python's tuple unpacking is extremely flexible, so calling `(a, b) = foo()` a function with 2 return values can get especially hazy due to the fact that, if `bar` returns the right format, this works: `((a, b), c, ((d, e), f)) = bar()` (Just, don't ever do that! Please!) – Izkata Jul 03 '13 at 02:01
  • 3
    @BlueRaja-DannyPflughoeft, if a tuple is "multiple values", why is a byte not considered "8 values"? Why is a string (for those languages that have strings other than pointers) not considered multiple values (1 value, or 8 values, for each character). – AMADANON Inc. Jul 03 '13 at 03:09
  • 21
    ...I never claimed that tuples couldn't be used for other things. But arguing that *"Python does not support multiple return values, it supports tuples"* is simply being **extremely** pointlessly pedantic. This is still a correct answer. – BlueRaja - Danny Pflughoeft Jul 03 '13 at 03:25
  • 1
    You don't need classes, you just need structured data. It's not an OOP concept. – OrangeDog Jul 03 '13 at 08:56
  • 3
    @BlueRaja-DannyPflughoeft: the difference has practical implications, though. The fact that a tuple is "one thing" means you can assign both values to a single variable by mistake (e.g. `username = GetUsernameAndPassword()`). In a language with real multiple values, such as Go, that's marked as a syntax error. – André Paramés Jul 03 '13 at 13:40
  • 1
    A tuple, or any structured grouping, is different because it expresses a valid, innate relationship between the distinct [primitive] values, even if the relationship is tenuous. – JustinC Jul 03 '13 at 16:45
  • 15
    Neither tuples nor classes are "multiple values". – Andres F. Jul 03 '13 at 17:25
  • 1
    @AndresF.: If you don't consider tuples to be multiple values, is there anything you would consider to be multiple values? – Michael Shaw Nov 20 '13 at 21:37
  • @MichaelShaw Excellent question. I can't think right now of an example of multiple values. I just know a function with signature `f(x1: X1, ..., xn: Xn): T` returns a single value of type `T` (which may be a tuple `(T1,..., Tn)`, a class or a primitive like `Int`). – Andres F. Nov 20 '13 at 22:17
  • 3
    @AndresF.: I don't think there's a difference between the input to f, which is (x1, x2, ... xn) and a tuple (x1, x2, ... xn). It's consistent either way, but if you're going to call an output tuple only one output, it would make sense to call the inputs only one input. – Michael Shaw Nov 20 '13 at 23:20
  • @MichaelShaw Agreed. In a sense, it is a single input of type `X = (X1, ..., Xn)`. This makes special sense when you look at Haskell's `curry` and `uncurry` functions :) I think the OP was mostly asking about a technical limitation of some languages (the original Pascal? can't remember) which had functions which could only return values of primitive types. As soon as you can return records, classes, tuples or whatnot, problem solved. It was never about multiple return values, because a tuple _is_ a value. – Andres F. Nov 20 '13 at 23:40
  • 2
    @MichaelShaw BTW, once you start considering a tuple to have "multiple values", you must also consider functions that return a list, array (or even a byte, which can be seen as an array of bits!) to be multi valued. Which doesn't make much sense to me... – Andres F. Nov 20 '13 at 23:46
  • @AndresF.: I think it's a matter of perspective. Is a byte "really" 8 bits or "really" a byte? Both points of view make sense, although sometimes one or the other makes more sense in a particular context. – Michael Shaw Nov 20 '13 at 23:56
  • 3
    I agree. A tuple is not the same as multiple return values any more than returning an array, dict, or any other arbitrary object is. To me, the distinction is not very arbitrary: does the language return a reference to a single object or multiple references to multiple objects? Out parameters are the closest thing I've ever seen to this. – Mark E. Haase Dec 22 '13 at 18:27
  • Just for the reference, there is a language explicitly using multiple return values, all the time. Golang. The convention is to use the 2nd value to return the error. This way, people do: `val, err := func(); if (err != nil) [...]`. – Florian Margaine Dec 23 '13 at 12:44
  • 2
    Another answer where I wish I could downvote comments. Do all functional programmers spend more time arguing over petty semantics, or just the ones that frequent this site? A tuple that doesn't require an explicit name is, for all practical purposes, *multiple values*, especially if the compiler lets you write it as something similar to `(x, y) = func(...)`. – Aaronaught Dec 23 '13 at 20:59
  • 1
    @Aaronaught What if the compiler lets you write `(x, y) = func(...)` but _also_ lets you write `w = f(...)` for the same function? Is `w` a single value or multiple ones? If it can be pattern-matched, then it's multiple values? – Andres F. Dec 24 '13 at 00:17
  • @AndresF.: I don't know, *what if*? Is it really important what you name it? In the first scenario, you're using one function to assign two variables. Whether that happens internally by way of a tuple or anonymous type, or whether the compiled code actually maintains an awareness of two registers or stack frames for the return values, seems largely academic and not very interesting to those of us who aren't in the compiler-writing business. It's like quibbling about stack vs. heap allocation; very few of us program at a low enough level for it to make any difference. – Aaronaught Dec 24 '13 at 01:09
  • @Aaronaught Oh, I agree _in practice_ it probably doesn't matter. `w` is a single value which can be pattern matched to extract the values of `x` and `y`, and that's all you need to know. But to the OP it probably mattered... – Andres F. Dec 24 '13 at 02:07
  • A function does not strictly speaking return an object, it returns a *reference* to the object. So a function which "returns a tuple" returns a single value: A reference to the tuple. In Python it may look like multiple values because of tuple unpacking, but tuple unpacking happens at the call site and is wholly independent from the function call - you can also unpack a local variable or literal tuple. So this has nothing to do with multiple return values. (I guess you could a argue generator function may return multiple values, though.) – JacquesB Dec 17 '16 at 11:08
  • @Aaronaught: A tuple is not the same as multiple individual values, it is an object in itself. For example you can a method on a tuple - this would not make any sense if you considered it multiple individual values. So it is a practical difference, not just semantic quibbling. – JacquesB Dec 17 '16 at 11:45
57

Because functions are mathematical constructs that perform a calculation and return a result. Indeed, much that's "under the hood" of not a few programming languages focuses solely on one input and one output, with multiple inputs being just a thin wrapper around the input - and when a single value output doesn't work, using a single cohesive structure (or tuple, or Maybe) be the output (though that "single" return value is composed of many values).

This has not changed because programmers have found out parameters to be awkward constructs that are useful in only a limited set of scenarios. Like with many other things, the support isn't there because the need/demand isn't there.

Telastyn
  • 108,850
  • 29
  • 239
  • 365
  • 1
    I'm not sure I'd say that the need/demand isn't there; I think it's more because the work-around solutions are good enough. – FrustratedWithFormsDesigner Jul 02 '13 at 21:19
  • 5
    @FrustratedWithFormsDesigner - this came up a little bit in [a recent question](http://programmers.stackexchange.com/questions/202932/named-output-parameters-vs-return-values). I can count on one hand the number of times I wanted multiple outputs in 20 years. – Telastyn Jul 02 '13 at 21:26
  • 62
    Functions in Mathematics and functions in most programming languages are two very different beasts. – tdammers Jul 02 '13 at 21:50
  • 17
    @tdammers in the early days, they were very similar in thought. Fortran, pascal and the like where heavily influenced by math more than computing architecture. –  Jul 02 '13 at 22:00
  • 9
    @tdammers - how so? I mean for most languages it boils down to lambda calculus in the end - one input, one output, no side effects. Everything else is a simulation/hack on top of that. Programming functions might not be pure in the sense multiple inputs may yield the same output but the spirit is there. – Telastyn Jul 02 '13 at 22:11
  • 3
    Most programming languages allow functions like `rand()` or `time()` which aren't functions in the mathematical sense. – dan04 Jul 03 '13 at 00:21
  • 2
    @Telastyn, except that in Lisp (based a wee bit on lambda calculus) we have multiple return values. E.g. floor. – Alan Shutko Jul 03 '13 at 03:00
  • 1
    @dan04: Haskell seems to do fine using the IO Monad for time functions. As for PRNGs: they take seed values (parameterless versions of those functions just use a default seed, and are deterministic). So, in the mathematical sense, they are functions. – Steven Evers Jul 04 '13 at 08:10
  • 4
    @tdammers: `Functions in Mathematics and functions in most programming languages are two very different beasts` - Some might say, "unfortunately." – Steven Evers Jul 04 '13 at 08:11
  • 17
    @SteveEvers: it's unfortunate that the name "function" took over in imperative programming, instead of the more appropriate "procedure" or "routine". In functional programming, a function resembles Mathematical functions much more closely. – tdammers Jul 04 '13 at 08:17
  • 5
    @SteveEvers: And technically, Haskell's IO monad is cheating; it allows the language to remain pure by offloading the impure parts to the underlying runtime. Basically, returning `IO Something` is a Haskell programmer's way of saying, "My code is still pure, I can't be blamed for the runtime using it as an excuse to trigger side effects!", much like a mobster might argue that he's innocent because he didn't actually kill anyone, just "suggested" that others do it for him. Still makes for very elegant code though, so I'm not actually arguing against Haskell here. – tdammers Jul 04 '13 at 08:21
  • 4
    @Telastyn: Conceptually, a function in Mathematics is a *morphism*, a mapping of values from one set to another. In imperative programming, a function consists of a body, which is just a sequence of statements and flow control constructs, a parameter list, and a return type. When a programming function is pure, it is isomorphic with the Mathematical function concept, but the mindset is still different. In pure functional programming, treating functions as Mathematical functions *does* make sense though, most of the time, and it's quite common to do so. – tdammers Jul 04 '13 at 08:26
  • @tdammers: Pascal does distinguish between `procedure` and `function`, but the latter can still be non-pure. – dan04 Jul 04 '13 at 14:58
  • @tdammers: Fair enough. +1 – Steven Evers Jul 04 '13 at 19:13
  • @Telastyn And have you been using languages which support multiple outputs in those 20 years? I never considered that I might want to partially apply functions until I started using Haskell. – Lexi Jul 10 '13 at 12:00
  • 1
    @LexiR - Yup. Personally, I often wanted to partially apply functions (though we called it binding or adaptation before Haskell was ever made). – Telastyn Jul 10 '13 at 13:25
  • @tdammers Those alternative names are too future-y. "Subroutine" is used on Star Trek, for example. ;) – Izkata Nov 20 '13 at 16:22
  • 3
    This is confusing two different concepts. In mathematics a function always returns one result, in the sense that if you call it again with the same arguments, you get the same result. However that one result can be a tuple or other structure with many components. Meanwhile in most programming languages a function can only return a single "thing", but if you call it again you might get a different "thing". (What a "thing" can be depends on the language) – Paul Johnson Dec 21 '13 at 22:42
  • Downvoting cause bringing in mathematical functions is largely irrelevant to a programming "function". – user949300 Dec 23 '13 at 05:01
  • 1
    Besides, functions that return a "single" item from cross-products sets like R² or R³ are extremely common. And what are those, if not mathematical functions with multiple outputs? – badp Dec 23 '13 at 10:35
  • 1
    @badp - a mathematical function that returns a single cohesive entity. In your case a set. Ugh, why can nobody understand this most elementary of concepts? Especially on a _programming_ site. – Telastyn Dec 23 '13 at 12:28
  • for a post pretending to be [based on math](http://en.wikipedia.org/wiki/Function_%28mathematics%29), this answer seems to lack precision and strictness – gnat Dec 23 '13 at 13:27
  • 2
    @tel you are assuming an awful lot here that is not true. f(x) = (True if x > 0 else False, df_3/dx, 3^x) is a perfectly valid function that goes from R to {True, False} × {set of all functions R to R} × R. I would hardly call that a single cohesive entity, yet it is a function nonetheless. – badp Dec 23 '13 at 13:46
  • @badp I disagree, your example is still a single output. Mathematical functions with "multiple outputs" do not exist by definition. This is highschool-level math! – Andres F. Dec 23 '13 at 21:30
  • @badp "A [function](http://en.wikipedia.org/wiki/Function_%28mathematics%29#cite_note-4) f from X to Y is a subset of the cartesian product X × Y subject to the following condition: every element of X is the first component of one and only one ordered pair in the subset". Your example complies with this and doesn't return "multiple outputs", which would violate the definition of function. – Andres F. Dec 23 '13 at 21:33
  • 1
    @and What is the difference between returning one thing with many components and returning many individually? None at all. The part where you can only return one element from Y is easily sidestepped by having f from X to [P](http://en.wikipedia.org/wiki/Power_set)(Y), set of all possible subsets of Y. You may be thinking about non-functions like, say, y = ±√(1 - x²) (the circle), but it's trivial to turn that into a function: f(x) = (+ √(1 - x²), - √(1 - x²)). I'm sure you can come up with all kinds of degenerate cases, but the principle remains true in all practical cases. – badp Dec 23 '13 at 22:50
  • Mathematically, the only truly important thing about a function is that it accepts _all_ the values of X (a set that you get to choose anyway). Only special cases of functions assert properties on Y, such as bijectivity. You don't want your program crashing on malformed input, but you don't really care about being able to generate all possible outputs, perhaps in a fully reversible mapping (_only then_ you no longer get to use the f → P(Y) "trick"). – badp Dec 23 '13 at 23:01
  • @badp Mathematically the only rule for functions is that they are relations constrained by the rule I quoted. Please not P(Y) (the powerset) is _a single element_; you are not side-stepping anything. – Andres F. Dec 23 '13 at 23:08
  • @badp I'm guessing your remark about "multiple outputs" is a confusion (either yours or of the motivating question) caused by early programming languages which had functions that could only return primitive types (such as `Integer`). Because programmers were used to thinking a tuple cannot be returned by a function, they incorrectly assumed a tuple is not "a single value". A tuple is a value. So is a set. Do not confuse technical limitations of programming languages with mathematical rules/definitions! – Andres F. Dec 23 '13 at 23:09
  • @And What part of my reasoning goes against the definition you copy-pasted from Wikipedia? – badp Dec 23 '13 at 23:12
  • @AndresF. The _whole point of the question_ is that there are some programming languages where you *are* restricted on your choice of Y, or where you can pick Y as {X × T × Z} ∪ E if you really want to but it's clunky or verbose or discouraged or you have to pretend the function is returning stuff while it's really changing what the input pointers point to or otherwise relying on side effects (e.g. global values), whereas something like Python has functions like [`divmod`](http://docs.python.org/2/library/functions.html#divmod) in its standard library. – badp Dec 23 '13 at 23:17
  • @badp I meant your definition doesn't side-step anything. The powerset is a single value; you have only one such value for every X in the domain of `f`, otherwise it cannot be a function. Again, this is highschool-level maths. I also argued that what people usually think of as "not returning multiple values" actually means (in technically limited languages) "only returning primitive values". I didn't mention pointers or calling conventions or any other implementation detail. – Andres F. Dec 23 '13 at 23:55
  • @and your calling out parameters implementation details further reinforces my belief that you still are very much missing the point of this question. – badp Dec 24 '13 at 00:00
36

In mathematics, a "well-defined" function is one where there is only 1 output for a given input (as a side note, you can have only single input functions, and still semantically get multiple inputs using currying).

For multi-valued functions (eg. squared root of a positive integer, for example), it's sufficient to return a collection, or sequence of values.

For the types of functions you're talking about (ie. functions that return multiple values, of different types) I see it slightly differently than you seem to: I see the need/use of out params as a workaround for better design or a more useful data structure. For example, I'd prefer if *.TryParse(...) methods returned a Maybe<T> monad instead of using an out param. Think of this code in F#:

let s = "1"
match tryParse s with
| Some(i) -> // do whatever with i
| None -> // failed to parse

Compiler/IDE/analysis support is very good for these constructs. This would solve much of the "need" for out params. To be completely honest, I can't think of any other methods off-hand where this wouldn't be the solution.

For other scenarios - the ones I can't remember - a simple Tuple suffices.

gnat
  • 21,442
  • 29
  • 112
  • 288
Steven Evers
  • 28,200
  • 10
  • 75
  • 159
  • 1
    In addition, I would really like to be able to write in C#: `var (value, success) = ParseInt("foo");` which would be compile-time type checked because `(int, bool) ParseInt(string s) { }` was declared. I *know* this can be done with generics, but still it would make for a nice language addition. – Grimace of Despair Jul 03 '13 at 12:30
  • 10
    @GrimaceofDespair what you really want is destructuring syntax, not multiple return values. – Domenic Jul 03 '13 at 18:39
  • Ok, let's vote then :P http://connect.microsoft.com/VisualStudio/feedback/details/371895/destructuring-assignment ... oh, it's already closed as "Won't fix" :D – Grimace of Despair Jul 04 '13 at 07:02
  • random question - does that make the function that solves a quadratic equation not "well-defined" because it returns a plus/minus value? – warren Jul 08 '13 at 19:31
  • 2
    @warren: Yes. See here and note that such a solution would not be continuous: http://en.wikipedia.org/wiki/Well-definition – Steven Evers Jul 08 '13 at 21:24
  • 6
    The mathematical notion of well-definedness has nothing to do with the number of outputs a function returns. It means that the outputs will always be the same if the input is the same. Strictly speaking, mathematical functions return one value, but that value is often a tuple. To a mathematician, there is essentially no difference between this and returning multiple values. Arguments that programming functions should only return one value because that's what math functions do aren't very compelling. – Michael Siler Dec 22 '13 at 03:39
  • @LorenPechtel: How would it be a breaking change? It's a simple method overload. The current version takes an `out` parameter; nothing is currently using the signature `TryParse(string s)`, which means it would be *adding* to the language, which means that it would, by definition, be a *non-breaking change*. Entirely irrelevant to the question, though. – Aaronaught Dec 23 '13 at 21:05
  • 1
    @MichaelSiler (I agree with your comment) But please note that argument is reversible: "arguments that program functions can return multiple values because they can return a single tuple value aren't very compelling either" :) – Andres F. Dec 24 '13 at 00:09
  • @Aaronaught You're right, with the out parameter gone the signature changes. I was thinking of Parse for some reason. – Loren Pechtel Dec 24 '13 at 01:35
23

In addition to what's already been said when you look at the paradigms used in assembly when a function returns it leaves a pointer to the returning object in a specific register. If they used variable/multiple registers the calling function would not know where to get the returned value(s) if that function was in a library. So that would make linking to libraries difficult and instead of setting an arbitrary number of returnable pointers they went with one. Higher level languages don't quite have the same excuse.

David
  • 501
  • 1
  • 3
  • 8
  • Ah! Very interesting point of detail. +1! – Steven Evers Jul 02 '13 at 22:02
  • This should be the accepted answer. Usually people think about target machines when they built compilers. Another analogy is why we have int, float, char/string, etc. because that's what's supported by target machine. Even if the target is not bare metal (e.g. jvm), you still want to get decent performance by not emulating too much. – imel96 Jul 02 '13 at 22:34
  • 26
    ...You could easily define a calling convention for returning multiple values from a function, in nearly the same way you can define a calling convention for passing multiple values to a function. This is not an answer. -1 – BlueRaja - Danny Pflughoeft Jul 02 '13 at 22:40
  • I do not agree this should be THE accepted answer, but I agree it is a good detail to know, and to add to the answer(s): it is a fact that common calling conventions on common architectures use a register (EAX in x86) for returning values to the caller. Different calling conventions are possible, of course, but... – Lorenzo Dematté Jul 05 '13 at 06:58
  • 2
    It would be interesting to know if stack-based execution engines (JVM, CLR) have ever considered/permitted multiple return values.. it should be quite easy; the caller just have to pop the right number of values, just like it pushes the right number of arguments! – Lorenzo Dematté Jul 05 '13 at 07:02
  • @BlueRaja-DannyPflughoeft That is a great point I had not considered, however do we not now have an arbitrary number of parameters we are limited to based on what compiler we're using? Yes it could technically work, I didn't say it couldn't be done. I just said (implied) that it would be very hairy for this reason and those already mentioned. – David Jul 08 '13 at 20:15
  • 1
    @David no, [cdecl](http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl) allows for (theoretically) unlimited number of parameters *(that is why [varargs functions](http://en.wikipedia.org/wiki/Varargs) are possible)*. Though some C-compilers may limit you to several dozen or hundred arguments per function, which I think is still more than reasonable -_- – BlueRaja - Danny Pflughoeft Nov 21 '13 at 00:30
  • @BlueRaja-DannyPflughoeft For some reason I guess I didn't consider using the stack in lieu of just registers as the current convention is. If I had to guess there might have been a reason why the designers didn't want to use more stack space or over use the small amount of registers. – David Nov 21 '13 at 04:44
  • @David I'm not sure what you mean by the "current convention," all calling conventions use the stack. `thiscall` *(the calling convention used by C++)* uses one register for the `this` pointer, and the rarely-used `fastcall` stores the first two arguments in registers. Everything is is on the stack. [See also](http://blogs.msdn.com/b/oldnewthing/archive/2004/01/08/48616.aspx) – BlueRaja - Danny Pflughoeft Nov 21 '13 at 06:50
  • I was talking about the return variable and a complete assumption about early hardware limitations. – David Nov 21 '13 at 23:20
  • This is definitely the most direct answer. I was about to post the same thing. Many answers here are concerned with mathematics, but most common programming languages don't have a whole heckuva lot in common with mathematics. x=x+1 is very common in many programs but completely illogical from an algebraic point of view. I would be fascinated to find out why the x86 ABI chose a single return register versus values on the stack. Although I can already guess the answer is (once again) rooted more in legacy than rational, intentional, clean room design. – Mark E. Haase Dec 22 '13 at 18:35
  • Why don't high-level languages have the same excuse? They still need to compile down to machine code eventually, and allowing an arbitrary number of return values (registers) would, at a minimum, make optimization a nearly impossible problem to solve. Compilers or jitters would have to either instantiate a bunch of anonymous types (for what benefit?) or leave it all on the stack. – Aaronaught Dec 23 '13 at 21:09
  • @Aaronaught That was in reference to interpreted languages. Though I would say a JIT does get to be abstract enough that it could add a unique calling system that does allow for multiple return values. The likely reason it hasn't been added to many modern languages is the syntax (in my opinion ofcourse) is terrible. – David Dec 24 '13 at 01:48
  • Yes, you're probably right and that's (sort of) what I was getting at. Readability is the explicit aim of a high-level language, and it's hard to imagine any multiple-return-value syntax that wouldn't eventually result in horrible abuse and obfuscation, like a function that returns two boolean values, or a coordinate with the x and y positions swapped, or miscellaneous garbage like `(item1, item2, item3) = DoEverythingConceivable(string data)`. Of course you can get *close* to this *without* multiple return values, but it's adding fuel to the fire, like multiple inheritance. – Aaronaught Dec 24 '13 at 06:09
  • Seeing how other answers are being preferred over yours, I decided to [explain in more detail](http://programmers.stackexchange.com/a/222281/5719) why processor characteristics play a big role. – Daniel C. Sobral Dec 24 '13 at 20:23
19

A lot of the use cases where you would have used multiple return values in the past simply aren't necessary anymore with modern language features. Want to return an error code? Throw an exception or return an Either<T, Throwable>. Want to return an optional result? Return an Option<T>. Want to return one of several types? Return an Either<T1, T2> or a tagged union.

And even in the cases where you genuinely need to return multiple values, modern languages usually support tuples or some kind of data structure (list, array, dictionary) or objects as well as some form of destructuring bind or pattern matching, which makes packaging up your multiple values into a single value and then destructuring it again into multiple values trivial.

Here are a few examples of languages that do not support returning multiple values. I don't really see how adding support for multiple return values would make them significantly more expressive to offset the cost of a new language feature.

Ruby

def foo; return 1, 2, 3 end

one, two, three = foo

one
# => 1

three
# => 3

Python

def foo(): return 1, 2, 3

one, two, three = foo()

one
# >>> 1

three
# >>> 3

Scala

def foo = (1, 2, 3)

val (one, two, three) = foo
// => one:   Int = 1
// => two:   Int = 2
// => three: Int = 3

Haskell

let foo = (1, 2, 3)

let (one, two, three) = foo

one
-- > 1

three
-- > 3

Perl6

sub foo { 1, 2, 3 }

my ($one, $two, $three) = foo

$one
# > 1

$three
# > 3
Deduplicator
  • 8,591
  • 5
  • 31
  • 50
Jörg W Mittag
  • 101,921
  • 24
  • 218
  • 318
  • 1
    I think one aspect is that, in some languages (such as Matlab), a function can be flexible in how many values it returns; see [my comment above](http://programmers.stackexchange.com/questions/203471/why-do-most-programming-languages-only-support-returning-a-single-value-from-a-f#comment397551_203471). There any many aspects in Matlab I don't like, but this is one of the few (perhaps the only) feature I miss when porting from Matlab to e.g. Python. – gerrit Jul 03 '13 at 10:08
  • @gerrit: this could be solved with overloading. A function which returns a 4-tuple is a different type than a function which returns a 5-tuple. So, you do not necessarily *need* multiple returns for this, however multiple returns seem to be more common than overloading based on return type. – Jörg W Mittag Jul 03 '13 at 10:12
  • 1
    But what about dynamic languages like Python or Ruby? Suppose I write something like the Matlab `sort` function: `sorted = sort(array)` returns only the sorted array, whereas `[sorted, indices] = sort(array)` returns both. The only way I can think of in Python would be to pass a flag to `sort` along the lines of `sort(array, nout=2)` or `sort(array, indices=True)`. – gerrit Jul 03 '13 at 10:17
  • @gerrit: True, if the type information isn't there, then there's nothing you can do about it. – Jörg W Mittag Jul 03 '13 at 15:16
  • @gerrit IMO, if the function can return arbitrarily many values, then it should return a list. If the function can return a fixed variation of values, I guess it has a return type somewhat like `2Values | 3Values | 5Values`, which can -- and should -- be pattern-matched. – Andres F. Jul 03 '13 at 17:31
  • @gerrit Its been a while since I worked with Matlab (5+ years) but aren't the multiple return values simply syntactic sugar for returning an array of variable size? – Mike Cellini Nov 20 '13 at 21:47
  • 2
    @MikeCellini I don't think so. A function can tell with how many *output* arguments the function is called (`[a, b, c] = func(some, thing)`) and act accordingly. This is useful, for example, if calculating the first output argument is cheap but calculating the second one is expensive. I'm not familiar with any other language where the equivalent of Matlabs `nargout` is available run-time. – gerrit Nov 24 '13 at 20:41
  • 1
    @gerrit the correct solution in Python is to write this: `sorted, _ = sort(array)`. – Miles Rout Feb 04 '14 at 23:32
  • 1
    @MilesRout: And the `sort` function can tell that it doesn't need to compute the indices? That's cool, I didn't know that. – Jörg W Mittag Feb 04 '14 at 23:35
  • No, it still computes it obviously, it just gets effectively thrown away. – Miles Rout Feb 04 '14 at 23:39
  • 1
    @MilesRout: That's not equivalent to to gerrit's Matlab code, though. In Matlab, the function *can* check how many return values are used, and act accordingly. – Jörg W Mittag Feb 04 '14 at 23:49
  • How are any of the examples showing languages that don't return multiple values? You have 'f' returning a list of values (1,2,3). They are positionally assigned to the 3 vars -- and you show that $one got the first arg, and $three got the 3rd arg. (-1) – Astara May 28 '14 at 19:55
13

The real reason that a single return value is so popular is the expressions that are used in so many languages. In any language where you can have an expression like x + 1 you are already thinking in terms of single return values because you evaluate an expression in your head by breaking it up into pieces and deciding the value of each piece. You look at x and decide that it's value is 3 (for example), and you look at 1 and then you look at x + 1 and put it all together to decide that the value of the whole is 4. Each syntactic part of the expression has one value, not any other number of values; that is the natural semantics of expressions that everyone expects. Even when a function returns a pair of values it's still really returning one value that's doing the job of two values, because the idea of a function that returns two values that aren't somehow wrapped up into a single collection is too weird.

People don't want to deal with the alternative semantics that would be required to have functions return more than one value. For example, in a stack-based language like Forth you can have any number of return values because each function simply modifies the top of the stack, popping inputs and pushing outputs at will. That's why Forth doesn't have the sort of expressions that normal languages have.

Perl is another language that can sometimes act like functions are returning multiple values, even though it's usually just considered returning a list. The way lists "interpolate" in Perl gives us lists like (1, foo(), 3) which might have 3 elements as most people who don't know Perl would expect, but could just as easily have only 2 elements, 4 elements, or any greater number of elements depending on foo(). Lists in Perl are flattened so that a syntactic list doesn't always have the semantics of a list; it can be merely a piece of a larger list.

Another way to have functions return multiple values would be to have an alternative expression semantics where any expression can have multiple values and each value represents a possibility. Take x + 1 again, but this time imagine that x has two values {3, 4}, then the values of x + 1 would be {4, 5}, and the values of x + x would be {6, 8}, or maybe {6, 7, 8}, depending on whether one evaluation is allowed to use multiple values for x. A language like that might be implemented using backtracking much like Prolog uses to give multiple answers to a query.

In short, a function call is a single syntactic unit and a single syntactic unit has a single value in the expression semantics that we all know and love. Any other semantics would force you into weird ways of doing things, like Perl, Prolog, or Forth.

Geo
  • 349
  • 3
  • 11
10

As suggested in this answer, it is a matter of hardware support, though tradition in language design also plays a role.

when a function returns it leaves a pointer to the returning object in a specific register

Of the three first languages, Fortran, Lisp and COBOL, the first used a single return value as it was modeled on mathematics. The second returned an arbitrary number of parameters the same way it received them: as a list (it could also be argued that it only passed and returned a single parameter: the address of the list). The third return zero or one value.

These first languages influenced a lot on the design of the languages that followed them, though the only one which returned multiple values, Lisp, never gathered much popularity.

When C came, while influenced by the languages before it, it gave a great focus on efficient use of hardware resource, keeping a close association between what the C language did and the machine code that implemented it. Some of its oldest features, such as "auto" vs "register" variables, are a result of that design philosophy.

It must be also pointed out that assembly language was widely popular until the 80s, when it finally started to be phased out of mainstream development. People who wrote compilers and created languages were familiar with assembly, and, for the most part, kept to what worked best there.

Most of the languages that diverged from this norm never found much popularity, and, therefore, never played a strong role influencing the decisions of language designers (who, of course, were inspired by what they knew).

So let's go examine assembly language. Let's look first at the 6502, a 1975 microprocessor that was famously used by the Apple II and VIC-20 microcomputers. It was very weak compared to what was used in the mainframe and minicomputers of the time, though powerful compared to the first computers of 20, 30 years before, at the dawn of programming languages.

If you look at the technical description, it has 5 registers plus a few one-bit flags. The only "full" register was the Program Counter (PC) -- that register points to the next instruction to be executed. The other registers where the accumulator (A), two "index" registers (X and Y), and a stack pointer (SP).

Calling a subroutine puts the PC in the memory pointed to by the SP, and then decrements the SP. Returning from a subroutine works in reverse. One can push and pull other values on the stack, but it is difficult to refer to memory relative to the SP, so writing re-entrant subroutines was difficult. This thing we take for granted, calling a subroutine at any time we feel like, was not so common on this architecture. Often, a separate "stack" would be created so that parameters and subroutine return address would be kept separate.

If you look at the processor that inspired the 6502, the 6800, it had an additional register, the Index Register (IX), as wide as the the SP, which could receive the value from the SP.

On the machine, calling a re-entrant subroutine consisted of pushing the parameters on the stack, pushing PC, changing PC to the new address, and then the subroutine would push its local variables on the stack. Because the number of local variables and parameters is known, addressing them can be done relative to the stack. For example, a function receiving two parameters and having two local variables would look like this:

SP + 8: param 2
SP + 6: param 1
SP + 4: return address
SP + 2: local 2
SP + 0: local 1

It can be called any number of times because all the temporary space is on the stack.

The 8080, used on TRS-80 and a host of CP/M-based microcomputers could do something similar to the 6800, by pushing SP on the stack and then popping it on its indirect register, HL.

This is a very common way of implementing things, and it got even more support on more modern processors, with the Base Pointer that makes dumping all local variables before returning easy.

The problem, the, is how do you return anything? Processor registers weren't very numerous early on, and one often needed to use some of them even to find out which piece of memory to address. Returning things on the stack would be complicated: you'd have to pop everything, save the PC, push the returning parameters (which would be stored where meanwhile?), then push the PC again and return.

So what was usually done was reserving one register for the return value. The calling code knew the return value would be in a particular register, that would have to be preserved until it could be saved or used.

Let's look at a language that does allow multiple return values: Forth. What Forth does is keeping a separate return stack (RP) and data stack (SP), so that all a function had to do was pop all its parameters and leave the return values on the stack. Since the return stack was separate, it did not get in the way.

As someone who learned assembly language and Forth in the first six month of experience with computers, multiple return values look entirely normal to me. Operators such as Forth's /mod, which return the integer division and the rest, seem obvious. On the other hand, I can easily see how someone whose early experience was C mind find that concept strange: it goes against their ingrained expectations of what a "function" is.

As for math... well, I was programming computers way before I ever got to functions in mathematics classes. There is a whole section of CS and programming languages which is influenced by mathematics, but, then again, there's a whole section which is not.

So we have a confluence of factors where math influenced early language design, where hardware constraints dictated what was easily implemented, and where the popular languages influenced how the hardware evolved (the Lisp machine and Forth machine processors were roadkills in this process).

Daniel C. Sobral
  • 3,541
  • 1
  • 24
  • 21
  • @gnat The use of "to inform" as in "to provide the essential quality" was intentional. – Daniel C. Sobral Dec 24 '13 at 21:17
  • feel free to rollback if you feel strongly about this; per my reading [influence](http://www.merriam-webster.com/dictionary/influence) fits slightly better here: "affects... in an important way" – gnat Dec 24 '13 at 21:20
  • 1
    +1 As it points out, that the sparse register count of early CPUs compared to an abundant register set of contemporary CPUs (also used in many ABIs, e.g. x64 abi) might be a game changer and the formerly compelling reasons to only return 1 value might nowadays just be a historical reason. – BitTickler Sep 03 '16 at 08:02
  • I'm not convinced that early 8-bit micro CPUs have a lot of influence on language design and what things are assumed to be needed in C or Fortran calling conventions across any architecture. Fortran assumes you can pass array args (essentially pointers). So already you have major implementation problems for normal Fortran on machines like 6502, because of a lack of addressing-modes for pointer+index, like discussed in your answer and in [Why do C to Z80 compilers produce poor code?](https://retrocomputing.stackexchange.com/q/6095) on retrocomputing.SE. – Peter Cordes Sep 26 '18 at 04:28
  • Fortran, like C, also assumes you can pass an arbitrary number of args, and have random access to them and an arbitrary amount of locals, right? As you just explained, you *can't* do that easily on 6502 because stack-relative addressing isn't a thing, unless you abandon reentrancy. You can pop them into static storage. If you can pass an arbitrary arg list, you can add extra hidden parameters for return values (e.g. beyond the first) that don't fit in registers. – Peter Cordes Sep 26 '18 at 04:30
  • The `gfortran` manual says (https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html) that some platforms already do this for Fortran functions, passing an output pointer instead of returning a value like the C ABI would. If you give up on re-entrant functions, you can just have the caller read outputs from the static storage for that function's outputs, on architectures that don't efficiently support pointers. Or have the callee put return values beyond the first one on the stack and copy the return address below that. (Kind of opposite of callee pops args.) – Peter Cordes Sep 26 '18 at 04:31
  • TL:DR: this is not convincing. Any ISA that can efficiently support pointers and arbitrary-length arg lists can efficiently support a calling convention with arbitrary-length return lists by hidden pointer for return values that don't fit in registers. (As many return regs as call-clobbered regs, if you want.). Use-case: having `strcmp` / `memcmp` also return a pointer or length for the difference, instead of just the compare result, would make them massively more useful as optimized search building blocks. ([Why is string comparison so fast in python?](https://stackoverflow.com/q/49950747)) – Peter Cordes Sep 26 '18 at 08:45
  • @PeterCordes Not true. Give me examples. Remember that you can only use processors that were available when the language was created, so let's limit the search to CPUs no younger than 1980. – Daniel C. Sobral Sep 26 '18 at 17:08
  • Which specific claim are you saying is not true? PDP-11 dates from 1970, and is somewhat similar to m68k with multiple orthogonal registers, each of which can hold a pointer. Pointers in PDP-11 asm work essentially the way they do in C, because C was designed on PDP-11. Wikipedia implies (https://en.wikipedia.org/wiki/PDP-11) that having multiple registers was new for PDP-11 over earlier PDP models, though, so this doesn't explain Fortran. But anyway, as far as calling-convention design options, PDP-11 is basically as flexible as m68k or x86. VAX also handles pointers just fine. – Peter Cordes Sep 26 '18 at 17:19
  • Examples of what? Do you want me to summarize a multi-return calling convention for PDP-11 with multiple return registers? Ok, the first return value goes in R0 (like normal I think), the next up to 3 go in R1..R3. Wide integers use up multiple slots, like for arg-passing rules. Return values after that are stored into memory pointed-to by hidden args (into temporaries, not into the final destination of the assignment unless escape analysis proves it won't modify a global before the function returns and degenerate into call-by-reference). – Peter Cordes Sep 26 '18 at 17:28
  • The fact that the normal calling convention didn't do this on PDP-11 is a consequence of C's design not requiring it, not that it's impossible. Any machine with multiple general-purpose registers can efficiently return a couple values in regs, and that's often all you care about for out-of-band error signalling, or a `memcmp` with difference, position. So arguments based on 8-bit micros that have a hard time implementing C or even regular Fortran aren't very convincing. I don't know enough about larger early CPUs, unfortunately, but I suspect they had addressing modes for C-like pointers. – Peter Cordes Sep 26 '18 at 17:34
  • @PeterCordes I actually programmed in all of the processors I mentioned. To put it plainly, you are wrong. Go read on 6502 assembly and see what I mean. Compiler efficiency was a major factor on language design -- on the languages that become popular at any rate, and often languages are designed based on languages that _were_ popular. These were the languages the people creating new languages knew, after all. – Daniel C. Sobral Sep 27 '18 at 19:21
  • But Fortran and C weren't designed on 6502 or other 8-bit micros. They were designed on more capable older machines, like one-per-university "mainframes" like PDP-11. If 6502 had any influence on the design of C, pointers wouldn't work the way they do, and reentrancy would have been a special feature rather then assumed for all functions unless you use `static` variables. According to [Why do C to Z80 compilers produce poor code?](https://retrocomputing.stackexchange.com/q/6095), and your answer here, passing some array args to a Fortran procedure isn't easy or efficient in 6502 either, but – Peter Cordes Sep 28 '18 at 01:11
7

The functional languages I know of can return multiple values easily through the use of tuples (in dynamically typed languages, you can even use lists). Tuples are also supported in other languages:

f :: Int -> (Int, Int)
f x = (x - 1, x + 1)

// Even C++ have tuples - see Boost.Graph for use
std::pair<int, int> f(int x) {
  return std::make_pair(x - 1, x + 1);
}

In the example above, f is a function returning 2 ints.

Similarly, ML, Haskell, F#, etc., can also return data structures (pointers are too low-level for most languages). I have not heard of a modern GP language with such a restriction:

data MyValue = MyValue Int Int

g :: Int -> MyValue
g x = MyValue (x - 1, x + 1)

Finally, out parameters can be emulated even in functional languages by IORef. There are several reasons why there is no native support for out variables in most languages:

  • Unclear semantics: Does the following function print 0, or 1? I know of languages that would print 0, and ones that would print 1. There are benefits to both of them (both in terms of performance, as well as matching the programmer's mental model):

    int x;
    
    int f(out int y) {
      x = 0;
      y = 1;
      printf("%d\n", x);
    }
    f(out x);
    
  • Non-localized effects: As in the example above, you can find that you can have a long chain and the innermost function affects the global state. In general, it makes it harder to reason about what the requirements of the function are, and if the change is legal. Given that most modern paradigms try to either localize the effects (encapsulation in OOP) or eliminate the side-effections (functional programming), it conflicts with those paradigms.

  • Being redundant: If you have tuples, you have 99% of the functionality of out parameters and 100% of idiomatic use. If you add pointers to the mix you cover the remaining 1%.

I have trouble naming one language which could not return multiple values by using a tuple, class or out parameter (and in most cases 2 or more of those methods are allowed).

KChaloux
  • 5,773
  • 4
  • 35
  • 34
Maciej Piechotka
  • 2,465
  • 2
  • 18
  • 19
  • +1 For mentioning how functional languages handle this in an elegant & painless way. – Andres F. Jul 03 '13 at 17:29
  • 1
    Technically you are still returning a single value still :D (It's just that this single value is trivial to decompose into multiple values). – Thomas Eding Jul 12 '13 at 21:39
  • @ThomasEding: Technically I can also imagine having one argument (a tuple) which just is constructed behind the scene - the 'technically' here is just a naming. My guess is that such distinction is meaningless once you try to formalize it. – Maciej Piechotka Jul 12 '13 at 22:43
  • 1
    I would say that a parameter with real "out" semantics should behave as a compiler temporary which gets copied to the destination when a method exits normally; one with "inout" semantics variable should behave as a compiler temporary which is loaded from the passed-in variable on entry and written back on exit; one with "ref" semantics should behave as an alias. The so-called "out" parameters of C# are really "ref" parameters, and behave as such. – supercat Jan 28 '14 at 22:16
  • Only, that it is not really /that/ painless. In order to compose something like ``f: a -> b -> c * d`` with ``g: c -> d -> x`` you need to have a function ``glue: next -> c * d -> x = next c d``. This is "noise" compared to a hypothetical language which would return multiple values rather than a tuple. – BitTickler Sep 03 '16 at 07:43
  • 1
    The tuple "workaround" also does not come for free. It blocks optimization opportunities. If an ABI existed which allowed N return values to be returned in CPU registers, the compiler could actually optimize, instead of creating a tuple instance and constructing it. – BitTickler Sep 03 '16 at 08:12
  • 1
    @BitTickler there is nothing preventing returning first n fields of struct to be passed by registers if you control ABI. – Maciej Piechotka Sep 03 '16 at 10:11
6

I think it's because of expressions, such as (a + b[i]) * c.

Expressions are composed of "singular" values. A function returning a singular value can thus be directly used in an expression, in place of any of the four variables shown above. A multi-output function is at least somewhat clumsy in an expression.

I personally feel that this is the thing that's special about a singular return value. You could work around this by adding syntax for specifying which of the multiple return values you want to use in an expression, but it's bound to be more clumsy than the good old mathematical notation, which is concise and familiar to everyone.

Roman Starkov
  • 4,469
  • 3
  • 31
  • 38
4

It does complicate the syntax a little, but there's no good reason at the implementation level not to allow it. Contrary to some of the other responses, returning multiple values, where available, leads to clearer and more efficient code. I can't count how often I have wished I could return an X and a Y, or a "success" boolean and a useful value.

ddyer
  • 4,060
  • 15
  • 18
  • 3
    Could you provide an example of where multiple returns provide clearer and/or more efficient code? – Steven Evers Jul 02 '13 at 22:05
  • 3
    For example, in C++ COM programming, many functions have one `[out]` parameter, but virtually all return an `HRESULT` (error code). It would be quite practical to just get a pair there. In languages which have good support for tuples, such as Python, this gets used in a lot of code I have seen. – Felix Dombek Jul 02 '13 at 22:08
  • In some languages, you'd return a [vector](http://mathworld.wolfram.com/Vector.html) with the X and Y coordinate, and returning any useful value would count as "success" with exceptions, possibly carrying that useful value, being used for failure. – doppelgreener Jul 02 '13 at 22:09
  • 3
    Lots of the time you end up encoding information into the return value in non-obvious ways - ie; negative values are error codes, positive values are results. Yuk. Accessing a hash table, it's always messy to indicate if the item is was found and also return the item. – ddyer Jul 02 '13 at 23:11
  • @SteveEvers The Matlab `sort` function normally sorts an array: `sorted_array = sort(array)`. Sometimes I also need the corresponding indices: `[sorted_array, indices] = sort(array)`. Sometimes I *only* want the indices: `[~, indices]` = sort(array)`. The function `sort` can actually tell how many output arguments are needed, so if additional work is needed for 2 outputs compared to 1, it can calculate those outputs only if needed. – gerrit Jul 03 '13 at 10:13
  • If you can return a tuple `(success, useful_value)`, you don't need multiple values. If your programming language has pattern matching, returning tuples is both elegant and painless. – Andres F. Jul 03 '13 at 17:26
  • @FelixDombek: That pattern exists purely from convention, and it's a terrible one. If the HRESULT is a failure code, what use does the `[out]` parameter have? It's been a long time since I cut any C++ COM code, but IIRC the answer is universally **none**. More specifically, the `[out]` parameter only has value when the `HRESULT` is success, so you're not realistically returning multiple values on every invocation. See my answer, or any write up on `Maybe` types for a clear solution to that scenario. – Steven Evers Jul 04 '13 at 07:41
  • @gerrit that's exactly what you'd do in Python. `sorted, indices = sort(array)`, `_, indices = sort(array)`, `sorted, _ = sort(array)`. Except that of course you shouldn't alias a built-in function (`sorted`) with a variable like that. Anyway, it's just tuples underneath. – Miles Rout Feb 04 '14 at 23:35
2

In most languages where functions are supported you can use a function call anywhere where a variable of that type can be used:-

x = n + sqrt(y);

If the function returns more than one value this will not work. Dynamically typed languages such as python will allow you to do this, but, in most cases it will throw up a run time error unless it can work out something sensible to do with a tuple in the middle of an equation.

James Anderson
  • 18,049
  • 1
  • 42
  • 72
  • 5
    Just don't use inappropriate functions. This is no different from the "problem" posed by functions that return no values, or return non-numeric values. – ddyer Jul 03 '13 at 04:27
  • 3
    In languages I've used that do offer multiple return values (SciLab, for example), the first return value is privileged, and will be used in cases where only one value is needed. So no real problem there. – The Photon Jul 03 '13 at 04:45
  • And even when they aren't, like with Python's tuple unpacking, you can select which one you want: `foo()[0]` – Izkata Nov 20 '13 at 16:54
  • Exactly, if a function returns 2 values, then it's return type is 2 values, not a single value. The programming language shouldn't read your mind. – Mark E. Haase Dec 22 '13 at 18:40
1

I just want to build on Harvey's answer. I originally found this question on a news tech site (arstechnica) and found an amazing explanation that I feel really answers the core of this question and is lacking from all the other answers(except Harvey's):

The origin of single return from functions lies in the machine code. At the machine code level, a function can return a value in the A (accumulator) register. Any other return values will be on the stack.

A language that supports two return values will compile it as machine code that returns one, and puts the second on the stack. In other words, the second return value would end up as an out parameter anyway.

It is like asking why assignment is one variable at a time. You could have a language that allowed a, b = 1, 2 for instance. But it would end up at the machine code level being a = 1 followed by b = 2.

There is some rationale in having programming language constructs bear some semblance to what will actually happen when the code is compiled and running.

user2202911
  • 153
  • 4
  • If low-level languages like C supported multiple return values as a first-class feature, C calling conventions would include multiple return-value registers, the same way that up to 6 registers are used for passing integer/pointer function args in the x86-64 System V ABI. (In fact x86-64 SysV does return structs up to 16 bytes packed into the RDX:RAX register pair. This is good if the struct was just loaded and will be stored, but costs extra unpacking vs. having separate members in separate regs even if they're narrower than 64 bits.) – Peter Cordes Sep 26 '18 at 08:58
  • The obvious convention would be RAX, then the arg-passing regs. (RDI, RSI, RDX, RCX, R8, R9). Or in the Windows x64 convention, RCX, RDX, R8, R9. But since C doesn't natively have multiple return values, C ABI/calling conventions only specify multiple return registers for wide integers and some structs. See [Procedure Call Standard for the ARM® Architecture: 2 separate, but related return values](https://stackoverflow.com/q/50897492) for an example of using a wide int to get the compiler to make efficient asm for receiving 2 return values on ARM. – Peter Cordes Sep 26 '18 at 08:59
-1

It started with math. FORTRAN, named for "Formula Translation" was the first compiler. FORTRAN was and is oriented to physics/math/engineering.

COBOL, nearly as old, had no explicit return value; It barely had subroutines. Since then it's been mostly inertia.

Go, for example, has multiple return values, and the result is cleaner and less ambiguous than using "out" parameters. After a little bit of use, it is very natural and efficient. I recommend multiple return values be considered for all new languages. Maybe for old languages, too.

RickyS
  • 23
  • 1
  • 4
    this does not answer the question asked – gnat Dec 22 '13 at 12:06
  • @gnat as for me it answers. OTOH one have already to need some background to understand it, and that person likely won't ask the question... – Netch Dec 22 '13 at 13:43
  • @Netch one hardly needs much background to figure that statements like _"FORTRAN... was the first compiler"_ are a total mess. It's not even wrong. Just like the rest of this "answer" – gnat Dec 22 '13 at 13:48
  • [link](https://en.wikipedia.org/wiki/Compiler#History) says there were earlier attempts at compilers, but that " The FORTRAN team led by John Backus at IBM is generally credited as having introduced the first complete compiler in 1957". The question asked was why only one? As I said. It was mostly mathematics and inertia. The mathematical definition of the term "Function" requires exactly one result value. So it was a familiar form. – RickyS Dec 24 '13 at 06:26
-2

It probably has more to do with the legacy of how function calls are made in processor machine instructions and the fact that all programming languages derive from machine code: for example, C -> Assembly -> Machine.

How Processors Perform Function Calls

The first programs were written in machine code and then later assembly. The processors supported function calls by pushing a copy of all of the current registers to the stack. Returning from the function would pop the saved set of registers from the stack. Usually one register was left untouched to allow the returning function to return a value.

Now, as to why the processors were designed this way... it was likely a question of resource constraints.

Harvey
  • 173
  • 1
  • 3