Re: 'switch-expression' and the 'type guard' unary operator demo
| From: | Mike Schinkel | Date: | Sun, 20 Oct 2019 21:12:35 +0000 |
| Subject: | Re: 'switch-expression' and the 'type guard' unary operator demo | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-107590@lists.php.net to get a copy of this message | ||
Hi Kosit,
Thank you for your reply.
> On Oct 20, 2019, at 3:54 AM, Kosit Supanyo <webdevxp.com@gmail.com> wrote:
>
> Hi Mike
>
> Is there a strong reason to change to using fat arrows and colons?
>
> The reason is statement/expression semantic.
Okay, so you chose commas and fat arrows because of perceived consistency. I can buy that.
However, I think by being consistent with one set of PHP syntax you ended up being inconsistent with
another set; the brace-enclosed block. In a brace-enclosed block statements are separated by
semi-colons, not commas. And given your use of
case we could argue that the
cases are sub-statements of the switch.
So I think both of our arguments as suspect here you are actually introducing a new syntactical
structure with this switch expression that has no current analogue in PHP. It is unlike anything
else that comes before it, and so I think it would be a mistake to be consistent in some ways but
not in others. Better to introduce rules that are specific to this structure that to justify the
rules based on only partial consistency.
Basically you are introducing the following syntax where the entirety is an expression:
<expression-designating-keyword> [(<selector-expression>)] {
<brace-enclosed-expression> }
And we should explicitly consider what are the best rules to use for this rather than allow the
rules to be defined based on consistency with something that is not like this new structure and thus
potentially paint us into a corner in the future simple because we did not evaluate it fully.
The question I think we should consider here is, what makes for a valid
<brace-enclosed-expression>? Is it a single expression, or can be be multiple expressions
with a delimiter where the last expression produces the resultant value? Or can it even be multiple
statements, the last of which produces the resultant value?
We can limit to single expressions, but that seems, well, limiting. If we instead leave open the
option for multiple statements — which a semi-colon would not conflict with — then we leave open
the future option to support multiple statements in a block-enclosed expression. But if we choose a
comma we can never go down that path. Given this, I think using comma paints us into a corner that
using semi-colon would not.
As for fat arrows vs. colons, that too feels like misapplication of consistency. A
case is more like a ternary operator than an array initializer, and is much more like a
switch statement. I fear that use of a fat arrow will also paint us into a corner, but I cannot
think of a specific example yet, it is just a fear based on my gut feeling. Using a colon would
likely not paint us into a corner because it has already been used in effectively exactly the same
context, e.g. the case statement for a switch.
So the question is, do we want to be consistent with an expression but inconsistent with
brace-enclosed blocks, or do we want to recognize that this is a new type of element and design the
best rules we can for this new type of element?
We can either limit a brace-enclosed expression to containing only simple expressions — which as I
said seems too limiting — or we can enable the future potential for multi-line statements in a
block-enclosed expression.
And that is my 200 cents on the matter. :-)
>> What about break?
>
> Let's see this example.
>
> for ($i = 0; $i < 100; $i++) {
> $x = getX($i);
> doSomething(
> $i,
> $x,
> switch ($x) {
> case 0 => 'ZERO',
> case 1 => 'ONE',
> case 2 => 'TWO',
> case 3 => 'THREE',
> default => break,
> },
> );
> }
>
> This should break switch or
> for? And if it breaks switch NULL
> should be returned but this will be the same as omitting default.
> And if it breaks for, call to
> doSomething() will be incomplete because a call frame was already
> pushed to VM stack. As I know ongoing function calls can only be aborted by exceptions (and
> return should not be allowed either, I need to fix that) so
> expressions must give something or throw.
As I envision, it should break for. And since the fact the call frame is already
pushed does not stop a throw from working I do not see why it should stop a
break from working.
And if for some reason that is too complicated, I do not see any reason why it could not throw an
error in that context, because the refactored solution is trivial, and I would say, preferred for
its readability anyway:
for ($i = 0; $i < 100; $i++) {
$x = getX($i);
$y = switch ($x) {
case 0 => 'ZERO',
case 1 => 'ONE',
case 2 => 'TWO',
case 3 => 'THREE',
default => break,
},
doSomething( $I, $x, $y );
}
BTW, when I code I use break wherever possible instead of throw[1-6], and
only ever use throw and try...catch when I am forced to. So supporting
throw but not break here would require an additional comparison after the switch
expression for people who like me avoid throw, which is not ideal.
-Mike
[1] http://www.lighterra.com/papers/exceptionsharmful/
<http://www.lighterra.com/papers/exceptionsharmful/>
[2] https://sidburn.github.io/blog/2016/03/25/exceptions-are-evil
<https://sidburn.github.io/blog/2016/03/25/exceptions-are-evil>
[3] http://xahlee.info/comp/why_i_hate_exceptions.html
<http://xahlee.info/comp/why_i_hate_exceptions.html>
[4] https://www.joelonsoftware.com/2003/10/13/13/
<https://www.joelonsoftware.com/2003/10/13/13/>
[5] https://mortoray.com/2012/04/02/everything-wrong-with-exceptions/
<https://mortoray.com/2012/04/02/everything-wrong-with-exceptions/>
[6] https://www.atlassian.com/blog/archives/exceptions_are_bad
<https://www.atlassian.com/blog/archives/exceptions_are_bad>