Call for bikeshed -- break replacement in expression switch

Kevin Bourrillion kevinb at google.com
Mon May 13 21:55:34 UTC 2019


Moving away from "break": I'm interested....

So in colon-form switch (whether statement or expression) you are
responsible for your own control flow, and in arrow-form switch (whether
statement or expression) you are not. "break" is synonymous in users' minds
with that control flow they don't want to have to do. So in theory it's
arrow-form that should make the concept of "breaking" obsolete.
Unfortunately, that doesn't seem like the distinction we'll making; do I
have the following right?

   1. A colon-form case in a switch statement stays absolutely the same as
   always - keep `break`ing
   2. An arrow-form case in a switch statement usually doesn't need to
   `break`... but can, just as an early-out from a block, right?
   3. A colon-form case in a switch expression cannot `break` at all; it
   either yields, throws, or falls through
   4. An arrow-form case in a switch expression: cannot `break` or fall
   through; must be a single expression, or it must always `__yield` or throw

So using break or not isn't about whether you are doing your own control
flow or not. So it's not a nice conceptual clean break that way, but in
practice we think most switches will be all #1 or all #4, do we not?

(side note: I had to think about these as four different kinds of switches.
As I think users will much of the time; it would be very optimistic to
think they will see it the way language designers do: two orthogonal
features that they can simply compose together or use apart. Actually they
won't see four kinds; they will think there are two kinds and then be very
surprised when they come across a hybrid like 2 or 3. )

Anyway, I don't dislike yield even though I know it has other connotations.
I think it communicates "I am done and I give forth this value", and what
happens from there can be context-dependent and that seems fine....



*From: *Brian Goetz <brian.goetz at oracle.com>
*Date: *Mon, May 13, 2019 at 6:33 AM
*To: *amber-spec-experts

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.)
>
>
>
>
>

-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20190513/e6fcfee1/attachment-0001.html>


More information about the amber-spec-experts mailing list