Multiple return values

Brian Goetz brian.goetz at
Mon Jan 14 16:29:54 UTC 2019

I was trying to keep my reply focused on concepts (nominal product types 
rather than tuples), rather than ad-hoc syntactic tricks (which have the 
risk of blurring the distinction.)

You're basically proposing several things:
  - Define a distinguished sub-category of records that have extra features;
  - For that sub-category, provide an ad-hoc, tuple-like construction 
  - For that sub-category, provide an ad-hoc, tuple-like destructuring 

I think the first is a mistake, and I don't think we really need either 
of the latter two -- and I think having them may well be more 
confusing.  If you're returning a Range, then saying

     x -> new Range(x,x+10)

is saying what you mean, and it's not very painful.

Similarly, there's no need to have an ad-hoc destructuring syntax for 
records; we _already_ have that, which is pattern matching. Whatever the 
syntax of "unconditional bind" is going to be, you'd be able to say 
something like

     __unconditional_bind Range(var lo, var hi) = getRange(...)

with an ordinary destructuring pattern.  No need for an explicit 
pseudo-tuple concept, and no need for an ad-hoc tuple-like destructuring 

If, at the end of the game, we decide that the straight denotation of 
pattern matching isn't enough, we can revisit.  But the answer to Lukas' 
question is basically:

  - Multiple return is a weak feature, that will feel like tuples 
dangled in front of your face, and then snatched back;
  - We prefer nominal tuple (records) to structural ones;
  - Records will have a compact (usually one line) declaration, a 
compact construction syntax, and a compact destructuring syntax.

On 1/14/2019 6:20 AM, Remi Forax wrote:
> You can have both !
> This is basically what we are doing with lambdas, you have a structural syntax + a named type that are bound together using inference.
> Let say we have a tuple keyword that means, value + record + constructor/de-constructor
>    tuple Range(int lo, int hi) { … }
> then you can write:
>    Range method(int x) {
>      return (x, x + 1);  // the compiler infers "new Range(x, x + 1)"
>    }
> and also
>    var (x, y) = method();  // the compiler uses the de-constructor or the record getters if there is no de-constructor
> With Stream<???> s =, ??? is a Range,
> and if someone want to use the tuple syntax inside a call to map(), a type as to be provided,
> by example
>    aStream.<Range>map(x -> (x, x + 1)).collect(...)
> Rémi
> ----- Mail original -----
>> De: "Brian Goetz" <brian.goetz at>
>> À: "Lukas Eder" <lukas.eder at>
>> Cc: "amber-spec-comments" <amber-spec-comments at>
>> Envoyé: Vendredi 11 Janvier 2019 17:07:43
>> Objet: Re: Multiple return values
>> While I understand where you’re coming from, I think multiple return is likely
>> to be both more intrusive and less satisfying than it first appears.
>> First, it’s a relatively deep cut; it goes all the way down to method
>> descriptors, since methods in the JVM can only return a single thing.  So what
>> you’re really asking the compiler to do is create an anonymous record (whose
>> denotation must be stable as it will be burned into client classfiles.)  That’s
>> the “more intrusive” part.
>> The “less satisfying” part is that if you can return multiple values:
>>     return (x, y)
>> and then obviously you need a way to destructure multiple values:
>>     (x, y) = method()
>> (since otherwise, what would you do with the return value?)
>> But here’s where people will hate you: why can I use tuples as return values,
>> and destructure them into locals, but not use them as method arguments, or type
>> parameters?  Now I can’t compose
>>     someMethod(method())
>> because I can’t denote the return type of method() as a parameter type.  And I
>> can use your multiple-returning method in a stream map:
>>     Stream<T> s =   // stream of what?
>> When we tug on this string, we’ll be very disappointed that it’s not tied to
>> anything.
>> Instead, what you can do is expose records in your APIs:
>> ```
>> class MyAPI {
>>     record Range(int lo, int hi) { … }
>>     Range method() { … }
>> }
>> ```
>> and now a caller gets a Range back, which is a denotable type and whose
>> components have descriptive names.
>> You say you don’t want to do this because creating new types is so much work.
>> Is the one-line declaration of `Range` above really so much work?  (Ignoring
>> the fact that returning a Range is far more descriptive than returning an (int,
>> int) pair.)
>>> On Jan 11, 2019, at 10:57 AM, Lukas Eder <lukas.eder at> wrote:
>>> Hello,
>>> I'm referring to the exciting proposed new features around destructuring
>>> values from records and other types as shown here:
>>> The example given was:
>>>   Rect r = ...
>>>   __let Rect(var p0, var p1) = r;
>>>   // use p0, p1
>>> This is a very useful construct, which I have liked using in other
>>> languages a lot. Just today, I had a similar use case where I would have
>>> liked to be able to do something like this, but without declaring a nominal
>>> type Rect. Every now and then, I would like to return more than one value
>>> from a method. For example:
>>>   private X, Y method() {
>>>     X x = ...
>>>     Y y = ...
>>>     return x, y;
>>>   }
>>> I would then call this method as follows (hypothetical syntax. Many other
>>> syntaxes are possible, e.g. syntaxes that make expressions look like
>>> tuples, or actual tuples of course):
>>>   X x, Y y = method();
>>> The rationale is that I don't (always) want to:
>>> - Modify either type X or Y, because this is just one little method where I
>>> want to indicate to the call site of method() in what context they should
>>> interpret X by providing a context Y
>>> - Wrap X and Y in a new type, because creating new types is too much work
>>> - Wrap X and Y in Object[] because that's just dirty
>>> - Rely on escape analysis for some wrapper type (minor requirement for me)
>>> - Assign both X and Y. Something like "X x, _ = method()" or "_, Y y =
>>> method()" would be useful, too.
>>> I was wondering if in the context of all the work going on in Amber around
>>> capturing local variables, etc. if something like this is reasonably
>>> possible as well in some future Java.
>>> This is possible in Python. Go uses this syntax to return exceptions.
>>> Thanks,
>>> Lukas

More information about the amber-spec-observers mailing list