27

It seems like in most mainstream programming languages, returning multiple values from a function is an extremely awkward thing.

The typical solutions are to make either a struct or a plain old data class and return that, or to pass at least some of the parameters by reference or pointer instead of returning them.

Using references/pointers is pretty awkward because it relies on side effects and means you have yet another parameter to pass.

The class/struct solution is also IMHO pretty awkward because you then end up with a million little classes/structs that are only used to return values from functions, generating unnecessary clutter and verbosity.

Furthermore, a lot of times there's one return value that is always needed, and the rest are only used by the caller in certain circumstances. Neither of these solutions allow the caller to ignore unneeded return types.

The one language I'm aware of that handles multiple return values elegantly is Python. For those of you who are unfamiliar, it uses tuple unpacking:

a, b = foo(c)  # a and b are regular variables.
myTuple = foo(c)  # myTuple is a tuple of (a, b)

Does anyone have any other good solutions to this problem? Both idioms that work in existing mainstream languages besides Python and language-level solutions you've seen in non-mainstream languages are welcome.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
dsimcha
  • 67,514
  • 53
  • 213
  • 334

14 Answers14

24

Pretty much all ML-influenced functional langues (which is most of them) also have great tuple support that makes this sort of thing trivial.

For C++ I like boost::tuple plus boost::tie (or std::tr1 if you have it)

typedef boost::tuple<double,double,double> XYZ;

XYZ foo();

double x,y,z;
boost::tie(x,y,z) = foo();

or a less contrived example

MyMultimap::iterator lower,upper;
boost::tie(lower,upper) = some_map.equal_range(key);
user21714
  • 5,781
  • 1
  • 20
  • 26
  • Indeed, Python's syntax is almost certainly deliberately taken from the functional programming world. – bobince Feb 05 '09 at 01:52
  • Awesome. I should really write something like this for D. If it can be done in C++, I'm sure it's trivial with D templates. – dsimcha Feb 06 '09 at 04:39
  • Yeah, I did it already. It was pretty trivial. I already was a heavy user of tuples, but getting the syntactic sugar for the unpacking right was non-trivial. – dsimcha Feb 08 '09 at 01:44
  • if you want to return only two values you can use std::pair as well – Janusz Jul 23 '09 at 00:08
  • 1
    Trivial, yes, but you lose meaning in your return. What does the second double of the tuple mean? Your guess is as good as mine. –  Oct 25 '10 at 12:06
9

A few languages, notably Lisp and JavaScript, have a feature called destructuring assignment or destructuring bind. This is essentially tuple unpacking on steroids: rather than being limited to sequences like tuples, lists, or generators, you can unpack more complex object structures in an assignment statement. For more details, see here for the Lisp version or here for the (rather more readable) JavaScript version.

Other than that, I don't know of many language features for dealing with multiple return values generally. However, there are a few specific uses of multiple return values that can often be replaced by other language features. For example, if one of the values is an error code, it might be better replaced with an exception.

While creating new classes to hold multiple return values feels like clutter, the fact that you're returning those values together is often a sign that your code will be better overall once the class is created. In particular, other functions that deal with the same data can then move to the new class, which may make your code easier to follow. This isn't universally true, but it's worth considering. (Cpeterso's answer about data clumps expresses this in more detail).

Moss Collum
  • 3,542
  • 3
  • 25
  • 23
  • 1
    The JavaScript one is awesome. Only newer browsers, though, of course. Can't use the dang thing yet. – Nosredna Feb 05 '09 at 03:57
  • 1
    Python's assignments supports any iterable: a, b, c = [0,1,2] or even generators like a, b, c = xrange(3) – dF. Feb 05 '09 at 12:13
  • 1
    Common Lisp also has `multiple-value-bind`. – Svante Feb 24 '09 at 22:39
  • The ML family of languages also have the feature you mention for Lisp and JS. Of course the ML family also gives you type safety :-) – harms May 28 '10 at 15:49
7

PHP example:

function my_funct() {
    $x = "hello";
    $y = "world";
    return array($x, $y);
}

Then, when run:

list($x, $y) = my_funct();
echo $x.' '.$y; // "hello world"
Evan Fosmark
  • 98,895
  • 36
  • 105
  • 117
  • Wow, that's actually a pretty nice way to do it. I was searching for whether returning an array or passing parameters by reference was the best solution. This seems to me the way I would want to do it. Thumbs up for PHP :) – Matthijs Wessels Oct 01 '10 at 09:07
6

If a function returns multiple values, that is a sign you might be witnessing the "Data Clump" code smell. Often data clumps are primitive values that nobody thinks to turn into an object, but interesting stuff happens as you begin to look for behavior to move into the new objects.

Writing tiny helper classes might be extra typing, but it provides clear names and strong type checking. Developers maintaining your code will appreciate it. And useful small classes often grow up to be used in other code.

Also, if a function returns multiple values, then it might be doing too much work. Could the function be refactored into two (or more) small functions?

Chris Peterson
  • 2,377
  • 1
  • 21
  • 24
  • 2
    I disagree completely. Yes, multiple return values are something bad programmers do. But *not everything has to have its own object or class*. Tuples are a perfectly valid data type in their own right, and to return a tuple of logically-connected values is just fine. – kquinn Feb 05 '09 at 01:18
  • 6
    A good example is a function that returns counts of something, eg, numUpdated, numDeleted, numUnchanged. Creating a class is overkill for 3 ints. WRT Java, its not too hard to create a Tuple class to handle the common n=2-3 cases (though its pretty verbose in practice). – Richard Levasseur Feb 05 '09 at 04:24
  • +1. I agree with commenters that native tuples are very nice to have when there really isn't additional object behavior, but I agree with the answer that it's worth a second thought when you find yourself returning multiple values, and this is good advice on what to think about. – Carl Meyer Feb 05 '09 at 17:57
  • 3
    Returning multiple unrelated values does smell bad, but multiple related values smell much better. – David Thornley Feb 05 '09 at 18:46
  • @ Richard Levasseur: I'd disagree. In your example, if you just return a tuple, it's easy to mix up the values (particularly as they are all of the same type). This is precisely where a class would help. Both when actually writing calls to the function, and when reading/debugging that code, because the meaning of the return values is explicit. – sleske Feb 09 '10 at 22:37
  • Python provides the `namedtuple` function to create tuples with attribute names on the fly. Being it python, the client code can retrieve the type if it wants, use the attributes (`t.cost`), or treat the value as a tuple by splitting, indexing, or doing tuple-arithmetic with it. – Apalala Feb 02 '11 at 01:30
4

Nobody seems to have mentioned Perl yet.

sub myfunc {
  return 1, 2;
}
my($val1, $val2) = myfunc();
Andru Luvisi
  • 24,367
  • 6
  • 53
  • 66
4

As for Java, see Bruce Eckel's Thinking in Java for a nice solution (pp. 621 ff).

In essence, you can define a class equivalent to the following:

public class Pair<T,U> {
    public final T left;
    public final U right;
    public Pair (T t, U u) { left = t; right = u; }
}

You can then use this as the return type for a function, with appropriate type parameters:

public Pair<String,Integer> getAnswer() {
    return new Pair<String,Integer>("the universe", 42);
}

After invoking that function:

Pair<String,Integer> myPair = getAnswer();

you can refer to myPair.left and myPair.right for access to the constituent values.

There are other syntactical sugar options, but the above is the key point.

joel.neely
  • 30,725
  • 9
  • 56
  • 64
3

i thinks python's is the most natural way, when I had to do same thing in php the only was to wrap return into an array. it does have similar unpacking though.

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
2

Even if you ignore the wonderful newer destructuring assignment Moss Collum mentioned, JavaScript is pretty nice on returning multiple results.

function give7and5() {
  return {x:7,y:5};
}

a=give7and5();
console.log(a.x,a.y);

7  5
Community
  • 1
  • 1
Nosredna
  • 83,000
  • 15
  • 95
  • 122
  • I, too, would like to use the newer syntax but it's not supported in IE7 or IE8 (forget about IE6). Your example suffices for my purposes. – Ray May 28 '10 at 16:18
1

Often in php I'll use a referenced input:

public function Validates(&$errors=array()) {
   if($failstest) {
      $errors[] = "It failed";
      return false;
   } else {
      return true;
   }
}

That gives me the error messages I want without polluting the bool return, so I can still do:

if($this->Validates()) ...
willoller
  • 7,106
  • 1
  • 35
  • 63
1

Both Lua and CLU (by Barbara Liskov's group at MIT) have multiple return values for all functions---it is always the default. I believe the designers of Lua were inspired by CLU. I like having multiple values be the default for calls and returns; I think it is a very elegant way of thinking about things. In Lua and CLU this mechanism is completely independent of tuples and/or records.

The portable assembly language C-- supports multiple return values for its native calling convention but not for the C calling convention. There the idea is to enable a compiler writer to return multiple values in individual hardware registers rather than having to put the values in a clump in memory and return a pointer to that clump (or as in C, have the caller pass a pointer to an empty clump).

Like any mechanism, multiple return values can be abused, but I don't think it is any more unreasonable to have a function return multiple values than it is to have a struct contain multiple values.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
0

Here was my Ruby idea to return a special value that looks and acts like a scalar but actually has hidden attributes on it:

https://gist.github.com/2305169

Peter Marreck
  • 53
  • 1
  • 3
0

c#

public struct tStruct
{
    int x;
    int y;
    string text;
    public tStruct(int nx, int ny, string stext)
    {
         this.x = nx;
         this.y = ny;
         this.text = stext;
    }
}

public tStruct YourFunction()
{
    return new tStruct(50, 100, "hello world");
}

public void YourPrintFunction(string sMessage)
{
    Console.WriteLine(sMessage);
}

in Lua you call

MyVar = YourFunction();
YourPrintfFunction(MyVar.x);
YourPrintfFunction(MyVar.y);
YourPrintfFunction(MyVar.text);

Output: 50 100 hello world

Paul

Paul
  • 1
0

By the way, Scala can return multiple values as follows (pasted from an interpreter session):

scala> def return2 = (1,2)
return2: (Int, Int)

scala> val (a1,a2) = return2
a1: Int = 1
a2: Int = 2

This is a special case of using pattern matching for assignment.

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
0

In C++ you can pass a container into the function so the function can fill it:

void getValues(std::vector<int>* result){
  result->push_back(2);
  result->push_back(6);
  result->push_back(73);
}

You could also have the function return a smart pointer (use shared_ptr) to a vector:

boost::shared_ptr< std::vector<int> > getValues(){
  boost::shared_ptr< std::vector<int> > vec(new std::vector<int>(3));
  (*vec)[0] = 2;
  (*vec)[1] = 6;
  (*vec)[2] = 73;
  return vec; 
}
Frank
  • 64,140
  • 93
  • 237
  • 324
  • BTW, everytime I deal with a pointer to a vector, I wonder why vec->[0] is not correct snytax and I have to write the ugly (*vec)[0]. Or am I missing something? – Frank Feb 05 '09 at 03:34
  • @dehmann: `[0]` is not a member of object pointed to by `vec`. `vec->[0]` means `(*vec).[0]` which is not what you want. – Lazer May 05 '10 at 19:20