> On Aug 13, 2020, at 5:37 PM, John Rose <john.r.r...@oracle.com> wrote: > > On Aug 13, 2020, at 12:39 PM, Guy Steele <guy.ste...@oracle.com > <mailto:guy.ste...@oracle.com>> wrote: >> >> Whereas I can more easily understand that the job of >> >> public deconstructor Point(int x, int y) { >> x = this.x; >> y = this.y; >> } >> >> is to take values out of the object “this” and put them into separate >> _variables_, not a new object. (Granted, these variables have a somewhat >> new and mysterious existence and character.) > > And if this mysterious character were something completely unrelated > to any other part of the Java language, I’d be more inclined to admit > that maybe the missing primitive is some sort of tuple. It might have > to be given a status like that of Java arrays to avoid the infinite regress > problem you point out. > > BUT, when I stare at a block of code that is setting some named/typed > variables, where those variables must be DA at the end of the block, > and then they are to be available in various other scopes (not the > current scope, but other API points), THEN I say, “where have I > seen that pattern before…?” There is ALREADY a well-developed > part of the Java language which covers this sort of workflow > (of putting values into a set of required named/typed variables). > > Of course, it’s a constructor,
Actually, a constructor _body_. Let us also recall that there is a second well-developed part of the Java language that puts values into a set of required named/types variables: method invocation. And its structure and behavior are rather different from that of a constructor body. (more below) > especially when the object has final > fields that are checked by the DA/DU rules. Now, maybe those rules > aren’t everybody’s favorite; they are complicated to learn. But > Java programmers do learn them. How about if we give them > additional rewards for having learned then? Instead of asking > them to learn yet another sub-language (tuples-for-deconstructors) > that is completely different? > > (Yes, I’m partial to DA/DU because those are my rules, for better > or worse. Maybe Remi’s going to say something about a sunk cost > fallacy. But I think the rules are useful and, with an IDE’s help, > intuitive. And they can do more for us now. Let’s double down > on them.) > > So here’s a principle to try out: > > The natural form of a multiple-value producing construct, in Java, > is a scope in which named/typed variables are in scope, are DU, > and must be DA before exit. > > As a variation, the natural form of a transactional multiple-value > consuming-and-producing construct, in Java, is a scope where > the values to be produced and consumed are both named/typed > variables (as above), and where if a name is to be produced and > consumed, it is a (mutable) DA value which is updated in the > scope, and if a name is to be consumed only it is an (immutable) > DA value, and otherwise the name to be produced only is DU > but must be DA at every (normal) block exit. > > Regarding “block exit”: A keyword like “return” (as in lambda) > or “yield” (as in e-switch) or “break” (as in s-switch) can provide > early return, exactly as today with constructors, and with suitable > DU/DA requirements on the variables. (A value-producing return > (or yield) could in some cases be take to return a right-typed > bundle. This might make sense, for example, with a constructor > for an inline type, which aborts the construction of the current > ’this’ in favor of some replacement value. This is illegitimate > for an identity class, but reasonable for an inline class.) > > The above proposed patterns make sense both internally to > a class (e.g., as constructor bodies which can touch private names) > or externally (for untrusted code outside the capsule), as some sort > of with-block construction, or (today) lambdas. > > Note that currently the natural form of a multiple-value consuming > construct in Java is a method or lambda body with formal parameter list > containing (wait for it) a set of named/typed variables in scope. > And/or a method body, where the named/type variables are instance > variables of the method’s class. Or both (there are extra axes of > variation here). > > On Aug 13, 2020, at 5:38 PM, John Rose <john.r.r...@oracle.com> wrote: > > What you said! > > On Aug 13, 2020, at 1:14 PM, Brian Goetz <brian.go...@oracle.com > <mailto:brian.go...@oracle.com>> wrote: >> >> By framing deconstructors as the arrow-reversed form of constructors, we >> provide the missing edges in our graph in a symmetrical way. By framing >> deconstruction as merely a "multi-getter", we may provide the missing edges, >> but the result is badly asymmetric. > All of which would seem to suggest Rémi’s multi-value-return minmax example as the dual to method invocation: >> . . . a method minMax that returns both the minimum and the maximum of an >> array >> public static (int min, int max) minMax(int[] array) { >> > Nope. Not going there. I went down this road too, but multiple-return is > another one of those “tease” features that looks cool but very quickly looks > like glass 80% empty. Part of the job of method invocation is to take a set of values and definitely assign them to a set of variables (the method parameters). This could be done with a block that is charged with the task of definitely assigning to those variables: Math.atan{ x = 2.0; y = 3.0 } myString.substring{ if (weird) { beginIndex = 3; endIndex = 5; } else { beginIndex = 0; endIndex = myString.length(); } } but for convenience (or for compatibility with C) we provide a different mechanism, with different syntax, that in effect uses positional tuples. A block-with-assignment mechanism is possible, but that’s not Java. Therefore we will keep re-encountering the question of why positional tuples are good Java style for passing several arguments to a method but not for returning several values from a method.