Skip to main content

Out params or multiple return values

22 replies [Last post]
jodonn
Offline
Joined: 2003-06-11
Points: 0

I'm getting rather sick of having to create new classes for the sole purpose of returning a pair of Strings from a method. Why not something like this instead:

...
String a, String b = doSomething();
...
/**
* ...
* @return A string.
* @return Another string.
*/
private String, String doSomething() {
return "yes", "no";
}

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
monika_krug
Offline
Joined: 2004-10-14
Points: 0

Okay, an example for multiple return values, where I would have needed them this week:
(Partially pseudo code, partially Java)

Assume, there is a class ExcelTable that needs a 2D array of type Cell upon initalization and can export these to an Excel file:

[code]public class ExcelTable
{
private Cell[][] table;
public ExcelTable(Cell[][] table)
{
this.table = table;
}
public void writeToFile(File file)
{
FileWriter fw = new FileWriter(file);
// write to Excel file here
}
}[/code]
There is a TableBuilder class that builds the 2D array of Cell from some data that is stored in a collection. (These two steps are separate so that arrays of Cell[][] could also be passed to e.g. HtmlTable instead of ExcelTable.)
[code]public class TableBuilder
{
public static Cell[][] buildTable(Collection c)
{
if (c==0 || c.size()==0) return new Cell[0][0];
int n = numberOfAttributesOfSomeType;
Cell[][] table = new Cell[c.size+1][n];
// header row
table[0][] = new Cell[] { new Cell("col1"), new Cell("col2"), ..., new Cell("coln") };
int cnt=1;
for (SomeType s : c)
{
for (int i=0; i {
table[cnt++][[b][/b]i] = s.getAttributeI();
}
}
return table;
}
}[/code]
Now things change. Data in the original table (that is stored in the collection) can not only consist of simple data types like String or Date, but of tables, too. These are to be embedded into the Excel file directly below the respective row.
That's fine so far, introduce some new classes:
[code]public class Table
{
Row[] r;
public Cell[][] flatten()
{
...
}
// constructors, accessors
}
public class Row
{
Cell[] data;
Table[] subtables;
int level;
boolean header;
// constructors, accessors
}[/code]
So far no problem.

But now I want to make the header rows of these subtables bold and the content of the subtables gray. This can only be done in the ExcelTable.writeToFile method. Now I need the information which rows are headers and which rows are not on level 1 there - but it was lost during Table.flatten when it was transformed into a Cell[][].

It's easy to record these two information in two List during flatten() and I would only have to add two for-loops to ExcelTable.writeToFile. I just can't pass the two arrays up.

What I will have to do eventually: Completely change the ExcelTable class to accept Table instead of Cell[][] in the constructor and then unwrap it into Cell[][] there instead of in the TableBuilder class. Sure, this is better design anyway. But I have to change a lot of code.

Monika.

dgriffit
Offline
Joined: 2004-10-13
Points: 0

The standard "hard" use case I've seen is in scientific computing, where it is not uncommon to have calculations you want more than one value out of (sin/cos, div/rem), and where creating a wrapper object really would be unnecessary overhead.

jarouch
Offline
Joined: 2004-03-04
Points: 0

But with this solution we will still need wrappers, which eats memory and CPU, and solving problem of primitives via autoboxing just make it worse.

So what does it give to us? Solution which can cover all cases, but is far from optimal. (It is similar as if we have Arrays.sort which can sloowlyy sort everything, but if you want you can use your own, better.;) )

May be i am wrong, but i think core api should be maximaly optimized and language should give us posibilities to write efective code and tend us to do it. M.R.V. is such thing, for example new autoboxing beloved by many for shorter code is not.

trinition
Offline
Joined: 2003-07-29
Points: 0

I disagree. Everytime I see this issue of multiple return values, everyone has trivial examples, such as "I want to swap two numbers".

The fact is, I have not seena ny provide a decent context where this is needed. I still assert until someone proves otherwise that this sort of problem can be solved with a better design rather than another feature added to the language. What is it you want to swap? What do they represent? For example, if its a point, you need a Point object with a method to mirror the point over the line x=y.

Do not start mucking with the language until you examined all possible solutions. Multiple return values to me is something I've never needed (yeah, I know, not objective). I've always found other, better solutions through proper design. Multiple return values would be the sort of solution that looks useful for a moment, but when you think about the critical thinking it eliminates in design, I think it could actually be harmful.

jarouch
Offline
Joined: 2004-03-04
Points: 0

Have you ever seen some complex example showing why we need new foreach cyclus? You just want go through list/array. And good old for cyclus is just another form of trivial usings of while. And we can continue..

If you see 'swap' so often, maybe it means it is thing every programmer have met. So one question to swap - how do you want swap local variables easilly? You will say - i put them to one wrapper with method swap. OK, but it only hides those 3 lines needed for swapping. And in each those wrapper you will have this 3 lines again and again and again... Plus you need write wrappers, instantialize wrappers, etc.. And you can hardly wrap f.e. two positions in array. Maybe some ArrayWrapperProvidingSwapping can help. Or just (a[i],a[i+1])=(a[i+1],a[i]). ;)

Btw. if swapping is so often, what about some special solution of this problem?

trinition
Offline
Joined: 2003-07-29
Points: 0

> Have you ever seen some complex example showing why
> we need new foreach cyclus? You just want go through
> list/array. And good old for cyclus is just another
> form of trivial usings of while. And we can
> continue..

I'm not in favor of the foreach cyclus (if that's the enhanced for loop) either. Where is the index variable? How do you know if there are more items? I think it was waste to have the langauge hide what it's doing underneath.

> If you see 'swap' so often, maybe it means it is
> thing every programmer have met. So one question to
> swap - how do you want swap local variables easilly?
> You will say - i put them to one wrapper with method
> swap. OK, but it only hides those 3 lines needed for
> swapping. And in each those wrapper you will have
> this 3 lines again and again and again... Plus you
> need write wrappers, instantialize wrappers, etc..
> And you can hardly wrap f.e. two positions in array.
> Maybe some ArrayWrapperProvidingSwapping can help. Or
> just (a[i],a[i+1])=(a[i+1],a[i]). ;)

Of the 3 lines you mention, which of their basic functionality will you not need inside the JVM's instruction set? Do you propose to add a new swap instruction? YOu always have to have the temporary assignment, if even to a register. And HotSpot can optimize it so that some variables are in registers.

Remember the old adviice to not optimize early? Is that was this cry for multiple return variables to enable swapping is -- premature optimization?

If not, I again ask for a real example where this will be truly useful. IF these multiple values are somehow related, don't loosely relate them in an untyped, unnamed collection -- give them meaning in a wrapper, or otherwise redesign.

jarouch
Offline
Joined: 2004-03-04
Points: 0

We have 2 posibilities - short and fast (optimalizable to 1 CPU instruction minimaly on x86 platform) and long and possibly fast (If Hotspot is smart enough).

Use simpler, shorter and faster possibility is not premature optimization, it is good style.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Agreed. This should be just enough. I just have not realized that :-)

Case closed. At least for me :-)

tsinger
Offline
Joined: 2003-06-10
Points: 0

Maybe more object-orientated design could help in 99% of the cases:

Variant 1:
[code]
Colour colour = obj.getColour();
// user colour.getA(), ... colour.getD();
[/code]

Variant 2:
[code]
Colour colour = ...
colour.doSomething(obj);
// user colour.getA(), ... colour.getD();
[/code]

Tom

brucechapman
Offline
Joined: 2004-03-18
Points: 0

-1

With this generic class
[code]class Pair {
final A first;
final B second;

Pair(A f, B s) {
first=f;
second=s;
}
}[/code]Your[code]private String, String doSomething() {
return "yes", "no";
} [/code]becomes[code]private Pair doSomething() {
return new Pair("yes", "no");
} [/code]With obvious siblings supporting the other common tuples (up to 4 say).

For primitives, either use auto boxing/unboxing, or roll your own wrapper if performance considerations justify it.

[b]Probably these generic tuple classes should be added to java.util so we don't all have to roll our own.[/b]

Addressing the other significant issues raised above...

> String a;
> a,_ = doSomething();
>if I didn't care what the second return of doSomething() was.
[code]String a=doSomething().first;[/code]
> It would not be unclear which return value would be used in the next message call
[code]doSomething().first.indexOf("e");
doSomething().second.indexOf("e");[/code]

monika_krug
Offline
Joined: 2004-10-14
Points: 0

You are right, that is a simple and good solution, no additional language features, just some more classes, if at all.

And now, .toString() (and maybe .length()) from the above example could be defined on the classes Pair, Triple, and Quadruple.

I think it is not even necessary to make f and s final. Why did you do that? What would the benefits be?

Monika.

vhi
Offline
Joined: 2004-10-11
Points: 0

To make sure that no one else modifies it. If you notice, the Pair makes its members non-private. So if it is not made final, another class using it may change its values unwantedly.

monika_krug
Offline
Joined: 2004-10-14
Points: 0

The point it that I think it would be just fine if they were changed.

scolebourne
Offline
Joined: 2003-06-20
Points: 0

There seem to be no really goo use cases for this. creating a string array or a small class is not hard to achieve this. Whereas the added language complexity is really quite high.

-1

jarouch
Offline
Joined: 2004-03-04
Points: 0

little comparation

with m.r.v.

(a, b, c, d) = obj.method();

without

arr = obj.method();
a = arr[0];
b = arr[1];
c = arr[2];
d = arr[3];

if you use wrapper you can have 20, maybe 50 lines of code with all those getters and comments.. More code, more used memory, probably slower execution.. Realy simple solution;)

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

Agreed, this would be useful, less verbose and more type safe.
I am not afraid of language complexity.

However, I see one issue with this: methods with multiple return values may not me used in chained method calls because it is not clear which return value should be used.

And any language construct used to resolve this issue is unacceptable increase in laguage complexity.

monika_krug
Offline
Joined: 2004-10-14
Points: 0

It would not be unclear which return value would be used in the next message call - all of them would be used.

[code]public static String, int, boolean foo()
{
return "abc", 5, true;
}

public static void bar(double d, String s, int i, boolean b, char c)
{
if (d>0 && b)
{
for (int cnt=0; cnt {
System.out.println(s+c);
}
}
}

public static void main(String[] args)
{
bar(17.0, foo(), 'x');
}[/code]See?

Maybe the notation needs to be worked on. The list of return values / variables to which they are assigned maybe should be surrounded by (..., ...) or {..., ...} or [..., ...].

While multiple return values are not an absolute necessity, they are helpful. Having to write wrapper classes for bundling up return values can be annoying and also confusing to a user of the original method. And I find myself working around it by ugly things like global variables to which I assign the additional return values, which is very un-OO.

Monika.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

This is really confusing. And what if you change order of the parameters in bar() method? yoou will have to change foo() also.

No thank you.

further. Consider this:

"result="+foo(); // what result would be added?
foo().toString(); // what object toString() is called on?
foo().length(); // ???

I am in favor of mutliple return values. But let's not attempt to solve this ambiguity. Compiund results simply cannot be used directly, you will have to split results into separate variables.

So all examples above are and should be illegal. I could live with that :-)

monika_krug
Offline
Joined: 2004-10-14
Points: 0

> This is really confusing. And what if you change
> order of the parameters in bar() method? you will
> have to change foo() also.

Well, if you now have a foo() method that returns a String and you change it to returning an int, you would also have to change bar if have a bar(foo()) call. So that's not news.

> further. Consider this:
>
> "result="+foo(); // what result would be added?
> foo().toString(); // what object toString() is called
> on?
> foo().length(); // ???
>
> I am in favor of mutliple return values. But let's
> not attempt to solve this ambiguity. Compiund results
> simply cannot be used directly, you will have to
> split results into separate variables.
>
> So all examples above are and should be illegal. I
> could live with that :-)

Yes, these examples should be illegal. Or .toString() and .length() on these on-the-fly compounds would have their own defined meaning and not mean the call of these methods of any of the parts of the compund.

Monika.

jarouch
Offline
Joined: 2004-03-04
Points: 0

So if you realy want it, you can vote here http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4222792 ;)

dgriffit
Offline
Joined: 2004-10-13
Points: 0

Generalize this to multiple assignment, allowing the rhs of an assignment to have multiple values, just like the left.

a,b = b,a;

should swap the values of b and a, without cluttering up the code with a temporary variable.

I would also include some special syntax for ignoring part of a multiple return, if you didn't need it. For instance

String a;

a,_ = doSomething();

if I didn't care what the second return of doSomething() was.

patrikbeno
Offline
Joined: 2004-10-11
Points: 0

not so bad.

I cant find any flaw. Syntax may need to be revisited, though.

not bad idea at all :-)