We’ve probably pretty much explored the options at this point;  time to 
converge around one of the choices...

> 
> De: "Brian Goetz" <brian.go...@oracle.com>
> À: "amber-spec-experts" <amber-spec-experts@openjdk.java.net>
> Envoyé: Dimanche 12 Mai 2019 21:38:38
> Objet: Call for bikeshed -- break replacement in expression switch
> As mentioned in the preview mail, we have one more decision to make: the new 
> spelling of “break value” in expression switches.  We have previously 
> discussed “break-with value”, which everyone seems to like better than “break 
> value”, but I think we can, and should, do better. 
> 
> (Despite the call-for-bikeshed, this is not to reopen every sub-decision — 
> the 2x2 semantics, the use of ->, the name of the construct — this bikeshed 
> only has room for one bike.)
> 
> There are two primary reasons why we prefer break-with to break.  We 
> originally chose “break value" when we had a more limited palette of options 
> to choose from (the keyword-resupply ship hadn’t yet docked.)  The 
> overloading of break creates uncomfortable interactions.  There is the 
> obvious ambiguity between “break value” and “break label”; there is also the 
> slightly less obvious interaction where we cannot permit “break value” inside 
> a loop or statement switch inside an expression switch.  While both of these 
> can be “specified around”, they create distortions in the spec, which in turn 
> creates complexity in the user model; these are a sign that we may be pushing 
> something a bit too far.  Further, historically “break” has been a straight 
> transfer of control; this muddies up what “break” means.  
> 
> Once we alit on the idea of break-* as a keyword, it seemed immediately more 
> comfortable to make a new break-derived keyword; this allowed us to undo the 
> distortions that “break value” introduced, and it immediately felt better.  
> But I think we can do better still.  Here’s what’s making me uncomfortable.  
> 
> We’ve actually been here before: lambda expressions were the first time we 
> allowed an expression to contain statements, and while the streamlined case 
> of “x -> e” didn’t require any control statements, and many lambdas could be 
> expressed with this form, statement lambdas needed a way to say “stop 
> executing the body of this lambda, and yield a value.”  We settled — somewhat 
> uncomfortably — on “return value" for this.  
> 
> Fast-forward to today, when we’re introducing the second expression form that 
> can contain statements, and we face the same question: how to indicate “I’m 
> done, I’m completing normally, here’s my value.”  Lambdas provide no help 
> here; we can’t use “return” here.  (Well, we could, but that would be 
> terrible, so we’re not going to.) Which means we have to solve the problem 
> again, but differently.  That’s already not so great.  
> 
> Digression: What’s so terrible about “return”, any why is it OK for lambdas 
> but not OK for switches?  
> 
> While we could of course define “return” to mean whatever we want, But, in 
> imperative languages with the concept of “methods” or “procedures”, including 
> Java, return has always had a clear meaning: unwind the current call frame, 
> and yield the designated value to the caller.  Lambda expressions are 
> effectively method bodies (lambdas are literals for functional interfaces, 
> which are single method interfaces), and so return (barely) fits.  But switch 
> expressions are most definitely not methods, and are not associated with call 
> frames. Asking users to look at the enclosing context when they see a 
> “return” in the middle of a method, to know whether it returns from the 
> method or merely transfers control within the method, is a lot to ask.  (Yes, 
> I know lambdas ask this as well; this is why this was an uncomfortable 
> choice, and having made this hole, I’m not anxious to expand it dramatically. 
>  If anything I’d prefer to close it, but that’s another bikeshed.). 
> 
> (end digression)
> 
> 
> We could surely take “break-with” and move on; it feels sufficiently 
> “switchy”.  But let’s look ahead a little bit.  We’ve now confronted the same 
> problem twice: an expression form that, in a minority use case, needed a way 
> to express “stop computing this expression, because I’m done, and here’s its 
> value.”   (And, unfortunately, we have two different syntactic ways to 
> express the same basic concept.)   Let’s call these “structured expressions.” 
>  
> 
> We have two structured expression forms, and of the three numbers in computer 
> science, “two” is not one of them.  Which suggests we are going to face this 
> problem again some day — whether it be “block expressions”, or “if 
> expressions”, or “let expressions”, or “try expressions”, or whatever.  (NB: 
> this call-for-bikeshed most definitely does not extend to “why not just do 
> generalized block expressions”, so please don’t go there.  That said, you 
> could treat this discussion as “if Java had block expressions, what might 
> they look like?”  But we’re focusing on the content of the block, not how the 
> block is framed.) 
> 
> Let’s say for sake of argument that we might someway want to extend ternary 
> expressions to support the same kind of “restricted block expressions” as 
> expression switches.  (This is just an example for purposes of illustration, 
> let’s not get derailed on “but you should use an ‘if’ statement for that"). 
> 
>     String s = (foo != null) 
>         ? s
>         : {
>              println(“null again at line” + __LINE__);
>              break-with “null”;
>           };
> 
> Such an expression needs a way to say “I’m done, here’s my value”, just as 
> lambda and switch did before it. Clearly “return” is not the right thing here 
> any more than it is for switches.  And I don’t think “break-with” is all that 
> great here either!  It’s not terrible, but outside of a loop or switch, it 
> starts to feel kind of forced.  And it would be terrible to solve this 
> problem twice with one-time solutions, and have no general story, and then 
> have to come up with YET ANOTHER way of expressing the same basic concept.  
> So regardless of what we expect for future expression forms, let’s examine 
> what our options are that are not tied to call frames (return) or direct 
> transfer of control (switches and loops.). 
> 
> Looking at what other languages have done here, there are a few broad 
> directions: 
> 
>  - A statement like “break-with v”, indicating that the enclosing structured 
> expression is completing normally with the provided value.  
>  - An operator that serves the same purpose, such as “-> e”.
>  - Assigning to some magic variable (this is how Pascal indicates the return 
> value of a function).  
>  - Treating the last expression in the block as the result.  
> 
> I think we can dispatch all but the first relatively easily:
> 
>  - We don’t use operators for “return”, we use a keyword; this would be both 
> a gratuitous departure, as well as too easy to miss.
>  - Switch expressions don’t have names, and even if we assigned to “switch”, 
> it wouldn’t be obvious that we were actually terminating execution of the 
> block. 
>  - Everywhere else in the language (such as method bodies), you are free to 
> yield up a value from the middle of the block, perhaps from within a control 
> construct like a loop; restricting the RHS of case blocks to put their result 
> last would be a significant new restriction, and would limit the ability to 
> refactor to/from methods. And further, the convention of putting the result 
> last, while a fine one for a language that is “expressions all the way down”, 
> would likely be too subtle a cue in Java.  
> 
> So, we want a keyword (or contextual keyword.).  In some hallway 
> brainstorming, candidates that emerged include yield, produce, offer, 
> offer-up, result, value-break, yield-value, provide, resulting-in, 
> break-with, resulting, yielding, put, give, giving, ...
> 
> (Also to keep in mind: remember we’re dealing with a minority case; most of 
> the time, there’ll just be an expression on the RHS.)
> 
> TL;DR: I think we might come to regret break-* just as we did with return — 
> because it won’t scale to future demands we place on it, and having *three* 
> ways to say basically the same thing in three different contexts would be 
> embarrassing.  I would like to see if we can do better.
> 
> 
> Of the options listed here, I have a favorite: yield.  (This is one of the 
> terms we’ve actually be using all along when describing this feature in 
> english.)  
> 
> There is one obvious objection to “yield”, which I’d like to preemptively 
> address: that in some languages (though not in Java, except for the 
> infrequently-used Thread.yield()), it is associated with concurrency 
> primitives, such as generators.  (This was the objection raised when yield 
> was proposed in the context of lambdas.). But, these association are not 
> grounded in existing Java constructs (and, the progress of Loom suggests that 
> constructs like async/await are not coming to Java, and even if we wanted 
> language support for generators, there are ample other ways to say it.)
> 
> Dictionary.com <http://dictionary.com/> lists the following meanings for 
> yield: 
> 
> verb (used with object)
>  - to give forth or produce by a natural process or in return for cultivation:
>  - to produce or furnish (payment, profit, or interest):
>  - to give up, as to superior power or authority:
>  - to give up or over; relinquish or resign:
>  - to give as due or required:
>  - to cause; give rise to:
> 
> verb (used without object)
>  - to give a return, as for labor expended; produce; bear.
>  - to surrender or submit, as to superior power:
>  - to give way to influence, entreaty, argument, or the like:
>  - to give place or precedence (usually followed by to):
>  - to give way to force, pressure, etc., so as to move, bend, collapse, or 
> the like:
> 
> These are mostly consistent with the use of “yield” as proposed here.  
> 
> One more thing to bear in mind: there is an ordering to abrupt completion 
> mechanisms, as to how far away they can transfer control:
> 
>  - yield: can unwind only the innermost yieldable expression
>  - break/continue: can unwind multiple control constructs (for, while, 
> switch), but stays within the method
>  - return: unwinds exactly one method
>  - throw: unwinds one or more methods
>  - System.exit: unwinds the whole VM
> 
> 
> Bikeshed is open (but remember the bounds of this bikeshed are limited; we’re 
> talking purely about the syntax of a “stop executing this block and yield a 
> value to the enclosing context” — and time is ticking.)
> 
> 
> 
> 
> 

Reply via email to