Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4222792

Please allow Multiple Return Values



    • Subcomponent:
    • CPU:
      generic, x86
    • OS:
      generic, windows_xp


      Name: dbT83986 Date: 03/22/99

      Java's second biggest problem, in my opinion, is the lack of support for multiple return values. I've personally written over 30,000 lines of
      Java code so far and have been heading up a large Java development project, so I do have some experience with Java programming.

      This lack of multiple return values has been a major pain. Whenever I want to return more than one result from a method, I am forced to allocate
      an object.

      The simple
        (mv-let (a b) (foo obj 17)
           ... )


        class TwoIntValues { int firstValue; int secondValue; }
        TwoIntValues ab = new TwoIntValues();
        a = ab.firstValue;
        b = ab.secondValue;
      or, if you don't want terrible memory waste,

        static myTwoIntValuesBuffer = new TwoIntValues();
          a = ab.firstValue;
          b = ab.secondValue;

      This leads to code bloat, and it becomes quite tricky to declared your buffer objects at appropriate scoping levels and such. Obviously you would like to eliminate the synchronized() above due to delays. But any attempt to do so usually results in all sorts of crazy invariants about who owns which buffers, and what to do with them.

      Perhaps worse, you end up defining meaningless classes like the above TwoIntValues class. I can understand geometric methods returning a Point, but why should a specialized divide operation have to return a QuotientAndRemainderIntInt? And should callers have to cache QuotientAndRemainderIntInt buffers here and there for fast access? This seems like a bad plan, seeing as all current processors have enough registers to return multiple values.

      I can understand why backwards languages like C++ haven't implemented multiple value returns: You can simulate it by passing a pointer to each return value. However, this leads to substantial inefficiency.

      Compare optimized implementations of these two calls:
        1. obj.foo(17,&a,&b) // No multiple value return
        2. (a,b) = obj.foo(17) // Multiple value return

      each followed by "return a+b":

      1. "obj.foo(17,&a,&b); return a+b;"

          sub sp,8
          mov r1,17
          lea r2,sp[0]
          lea r3,sp[4]
          call class$foo
          mov r1,sp[0]
          mov r2,sp[4]
          add r1,r2
          add sp,8
          ... compute r1 & r2 ...
          mov [r4],r1
          mov [r5],r2

      2. "(a,b)=foo(17); return a+b;"
          mov r1,17
          call class$foo
          add r1,r2
          .. compute r1 & r2 ...

      Not only is the second expression more readable, but it also allows the generation of much better code!

      In C++ we can do ugly Java-like tricks and come up with the more efficient code:

         struct TwoIntValues { int firstValue; int secondValue; };
         TwoIntValues ab = foo(17);
         a = ab.firstValue;
         b = ab.secondValue;

      But this is syntactically ugly and requires the definition of struct TwoIntValues to be in some public place. How much nicer if we just had an extended Java like this:

        void easy()
          int a;
          int b;
          (a,b)=foo(17); // Call
        (int,int) foo(int arg) // Declaration
          int x=...;
          int y=...;
          return (x,y); // Return value

      This extension seems nice and clean to me and it integrates easily into the existing language. The only drawback I can see here is that it C++ programmers may think "return (x,y);" returns the value of "y".

      An alternative syntax would be:

        Values(int,int) foo(int arg)
          return Values(x,y);

      in this circumstance I think it would be smart to press an unused Unicode character into service as a synonym for the "Values" operator, since it really shouldn't be looking like a class name.

      I've seen the complicated proposals here to declare some objects to be a "primitive" type so that they can be returned in registers, but these proposals introduce new, complex syntaxes and introduce a fundamental change to the Java object model that I consider inappropriate. They are also unsuitable for the task. They also fail to simplify the syntax or remove the need to declare "QuotientAndRemainderIntInt"-type classes.

      One other bit of syntactic convenience I recommend is to allow declarations inside the "Values" clause:

         void easy()
           Values(int a, int b) = foo(17);


      Multiple return values are quite useful in any language, and with Java's object model they are essential to generating efficient and readable code. No other construct I know of can take their place.

      They would be easy to implement. Even without suitable VM changes, the extra values could be stored at fixed TLS offsets. And future versions of the VM and Java native compilers could take advantage of register return values (either by recognizing the use of the "special" TLS slots in the byte code, or by having the compiler generate code based on the 1.3 VM spec, which has this ability built in).

      Think of it this way: Why should a method be allowed to receive 3 arguments but not allowed to return 3 results? To me, this seems to be a fundamental inconsistency in the language.
      (Review ID: 55623)


          Issue Links



              gbrachasunw Gilad Bracha (Inactive)
              dblairsunw Dave Blair (Inactive)
              0 Vote for this issue
              0 Start watching this issue