Hi Internals
I've just finished an implementation of 'switch-expression' that have been
discussed recently. So I would like to present my ideas here.
The basic syntax of switch-expression in this implementation is:
$result = switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};
Like function declaration and function expression in JavaScript, if
switch
appears as first token at statement level it will be recognized as
statement but if switch
is in expression context it will be
switch-expression.
switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};
// Parse error: syntax error, unexpected '=>' (T_DOUBLE_ARROW)
But this is OK.
!switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
}; // semicolon is still required because it is an expression
You can also use comma to associate multiple conditions with single result
expression.
$result = switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3, $cond4, $cond5 => $result3,
default => $default_result,
};
Which is equivalent to:
$result = switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
case $cond4 => $result3,
case $cond5 => $result3,
default => $default_result,
};
If there's no default value or switch-expression will just give NULL.
$x = 'c';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
};
var_dump($v); // NULL
Empty switch-expression is also allowed.
$v = switch ($x) {
};
var_dump($v); // NULL
You can omit parenthesized expression which is shortcut to switch (true)
.
This change applies to switch statement as well.
$v = switch {
case $x >= 0 && $x <= 100 => 1,
case $x >= 100 && $x <= 200 => 2,
default => 3,
};
switch {
case $x >= 0 && $x <= 100:
doSomething1();
break;
case $x >= 100 && $x <= 200:
doSomething2();
break;
default:
doNothing();
break;
}
You can also use return
and throw
in result expression. I recalled some
languages have this feature (but I've forgotten what language). This
feature can be very handy and useful in many use cases.
$x = 'd';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
case 'c' => return true,
default => throw new Exception("'$x' is not supported"),
};
Additional feature in the demo patch is the 'type guard' unary operator
which is an operator that will perform type check on given value and throw
TypeError
when type mismatch occurred, otherwise return the value as is.
It has the same precedence as new
.
$a = 'This is a string';
$v = <int>$a; // TypeError: Value is expected to be int, string given
Just like type hints, can accept nullable types.
$v = <?int>switch ($x) {
case 'a' => $result1,
case 'b' => $result2,
};
Also works with variable references.
$x = &<int>$a;
$y = &<callable>$functions[$name];
$arr = [&<Countable>$obj];
This operator can also be used to simulate typed variables.
The demo is here:
https://github.com/webdevxp/php-src
P.S. This is just a bunch of ideas with concrete demo, not intended to be
pull request nor proposal because I think I'm still new here and
inexperienced in many other ways.
Cheers
Hi Internals
I've just finished an implementation of 'switch-expression' that have been
discussed recently. So I would like to present my ideas here.
This is great to see that you proactively implemented this.
The basic syntax of switch-expression in this implementation is:
$result = switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};
However, why the need to deviate with the existing syntax, making refactoring between switch statements and switch expressions more work that it needs to be. Is there some reason why keeping colons and semlicolons would not work?
Given your syntax manually refactoring existing switches to use expressions, or refactoring back from expressions to statements will require more and tedious keystrokes than if you just kept colons and semicolons. Is there a strong reason to change to using fat arrows and colons?
You can omit parenthesized expression which is shortcut to
switch (true)
.
This change applies to switch statement as well.$v = switch {
case $x >= 0 && $x <= 100 => 1,
case $x >= 100 && $x <= 200 => 2,
default => 3,
};switch {
case $x >= 0 && $x <= 100:
doSomething1();
break;
case $x >= 100 && $x <= 200:
doSomething2();
break;
default:
doNothing();
break;
}
I do very much like this innovation.
Would love to see this work for switch statements too.
You can also use
return
andthrow
in result expression. I recalled some
languages have this feature (but I've forgotten what language). This
feature can be very handy and useful in many use cases.$x = 'd';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
case 'c' => return true,
default => throw new Exception("'$x' is not supported"),
};
What about break?
Additional feature in the demo patch is the 'type guard' unary operator
which is an operator that will perform type check on given value and throw
TypeError
when type mismatch occurred, otherwise return the value as is.
It has the same precedence asnew
.$a = 'This is a string';
$v = <int>$a; // TypeError: Value is expected to be int, string givenJust like type hints, can accept nullable types.
$v = <?int>switch ($x) {
case 'a' => $result1,
case 'b' => $result2,
};
Nice!
-Mike
Hi Mike
Is there a strong reason to change to using fat arrows and colons?
The reason is statement/expression semantic.
The reason for choosing comma over semicolon is because in PHP semicolons
are used only as statement separator while commas are used as expression
separator within brackets and item separator of some statements that can
take numbers of item such as echo
or variable/property declarations.
Since switch-expression is a list of expression pairs, commas should be
used to separate its items.
And the reason for choosing fat arrow over colon is still the same as
above, expression semantic. In PHP fat arrows (double arrows) were used
only to separate key and value until 7.4 where their role has extended
(arrow functions), while colons are used in three places labels, ternary
operator and return types. Since cases in switch statements are labels,
colons are approprate. But as I stated above, switch-expression is a list
of expression pairs so it should use syntax like array rather than its
statement counterpart. If PHP was using colon as key/value separator then
colons should be used here.
And here are examples of languages that use arrow-like syntax:
[C#]
var result = bank.Status switch
{
BankBranchStatus.Open => true,
BankBranchStatus.Closed => false,
BankBranchStatus.VIPCustomersOnly => isVip
};
[Rust]
let message = match x {
0 | 1 => "not many",
2 ..= 9 => "a few",
_ => "lots"
};
[Kotlin]
var result = when (number) {
0 -> "Invalid number"
1, 2 -> "Number too low"
3 -> "Number correct"
4 -> "Number too high, but acceptable"
else -> "Number too high"
}
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 shoud 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.
Cheers
On Oct 19, 2019, at 12:40 PM, Kosit Supanyo webdevxp.com@gmail.com
wrote:Hi Internals
I've just finished an implementation of 'switch-expression' that have been
discussed recently. So I would like to present my ideas here.This is great to see that you proactively implemented this.
The basic syntax of switch-expression in this implementation is:
$result = switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};However, why the need to deviate with the existing syntax, making
refactoring between switch statements and switch expressions more work that
it needs to be. Is there some reason why keeping colons and semlicolons
would not work?Given your syntax manually refactoring existing switches to use
expressions, or refactoring back from expressions to statements will
require more and tedious keystrokes than if you just kept colons and
semicolons. Is there a strong reason to change to using fat arrows and
colons?You can omit parenthesized expression which is shortcut to
switch (true)
.
This change applies to switch statement as well.$v = switch {
case $x >= 0 && $x <= 100 => 1,
case $x >= 100 && $x <= 200 => 2,
default => 3,
};switch {
case $x >= 0 && $x <= 100:
doSomething1();
break;
case $x >= 100 && $x <= 200:
doSomething2();
break;
default:
doNothing();
break;
}I do very much like this innovation.
Would love to see this work for switch statements too.
You can also use
return
andthrow
in result expression. I recalled some
languages have this feature (but I've forgotten what language). This
feature can be very handy and useful in many use cases.$x = 'd';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
case 'c' => return true,
default => throw new Exception("'$x' is not supported"),
};What about break?
Additional feature in the demo patch is the 'type guard' unary operator
which is an operator that will perform type check on given value and throw
TypeError
when type mismatch occurred, otherwise return the value as is.
It has the same precedence asnew
.$a = 'This is a string';
$v = <int>$a; // TypeError: Value is expected to be int, string givenJust like type hints, can accept nullable types.
$v = <?int>switch ($x) {
case 'a' => $result1,
case 'b' => $result2,
};Nice!
-Mike
Hi Kosit,
Thank you for your reply.
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 case
s 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
orfor
? And if it breaksswitch
NULL
should be returned but this will be the same as omittingdefault
. And if it breaksfor
, call todoSomething()
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 (andreturn
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
Hi Mike
In a brace-enclosed block statements are separated by semi-colons, not
commas.
Yes because they are statements, that's the point which I've stated many
times. And if your argument is that there's no place in PHP that uses
commas within curly braces, you're wrong. PHP already has one.
use X\Y\Z{
A,
B,
C,
};
The reason to choose what to be a separator is not about the container but
rather its items and semantics associated with them. And there's many
languages out there using commas to separate expressions within curly
braces if you did do some researches you might not think they are
inconsistent at all. In fact I'd already gave you some examples in previous
reply (C#, Rust).
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.
No one is limiting anything. I'm working on block expressions too. One of
them is eval
block which would be like:
$z = eval {
$x = doSomething1();
$y = doSomething2();
if ($x >= 0 && $y >= 0) {
give $x + $y;
}
give null; // can be omitted
};
If you think that anything inside curly braces must be separated by
semicolon, you are the one that limit yourself.
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 abreak
from working.
That's because throw
do different thing at the engine level not just
jumping things like break
, continue
or goto
.
BTW, when I code I use break
wherever possible instead of throw
[1-6],
and only ever use
throw
andtry...catch
when I am forced to. So
supporting throw but not break here would require an additional comparison
after theswitch
expression for people who like me avoidthrow
, which
is not ideal.
If break
is allowed in switch-expressions and its behavior is to break
outer loop, don't you think it could be an another inconsistency or
confusion? Because I'm 100% sure when people see break
within switch
they'll all think it should break switch
not something else.
Regards
Hi Kosit,
Thank you for your reply.
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 ofcase
we could argue that thecase
s 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. Acase
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. thecase
statement for
aswitch
.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
orfor
? And if it breaksswitch
NULL
should
be returned but this will be the same as omittingdefault
. And if
it breaksfor
, call todoSomething()
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 (andreturn
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 athrow
from working I do not see why it
should stop abreak
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 ofthrow
[1-6],
and only ever usethrow
andtry...catch
when I am forced to. So
supporting throw but not break here would require an additional comparison
after theswitch
expression for people who like me avoidthrow
, which
is not ideal.-Mike
[1] http://www.lighterra.com/papers/exceptionsharmful/
[2] https://sidburn.github.io/blog/2016/03/25/exceptions-are-evil
[3] http://xahlee.info/comp/why_i_hate_exceptions.html
[4] https://www.joelonsoftware.com/2003/10/13/13/
[5] https://mortoray.com/2012/04/02/everything-wrong-with-exceptions/
[6] https://www.atlassian.com/blog/archives/exceptions_are_bad
Yes because they are statements, that's the point which I've stated many times.
And I am suggesting that a block-enclose expression should not be limited to expressions only, but instead be able to use statements in the future. Using commas ensures we can never go that route.
And if your argument is that there's no place in PHP that uses commas within curly braces, you're wrong. PHP already has one.
use X\Y\Z{
A,
B,
C,
};
You have a point.
But that use-case is not a block, it is an obscure short-hand syntax for which nothing else in PHP is similar. I would not expect we should use it as a model unless it were for a very similiar thing.
if you did do some researches
Seriously?
you might not think they are inconsistent at all. In fact I'd already gave you some examples in previous reply (C#, Rust).
I did not state it before because I did not want to spend time on things I felt were irrelevant but the fact another language uses syntax does not make it appropriate for PHP. What makes it appropriate for PHP is "feel", consistency, and how sigils are already being used, and how they might be used in the future.
But I am not saying you do not look to other language for inspiration. What I am saying is you cannot look to other languages for validation, at least not in a vacuum.
No one is limiting anything. ... If you think that anything inside curly braces must be separated by semicolon,
Ok, so let's look at a scenario. Will we be able to do this in the future, if we decide to allow multiple expressions to result in a final value?
$y = switch ($x) {
case 1 => $arr = get_array(), array_pop($arr),
case -1 => $arr = get_array(), array_unshift($arr),
default => null,
};
What if in the future PHP implements variadic syntax for assigning to arrays, e.g. $var = 1,2,3 where $var could satisfy a $variadic parameter set of parameters w/o requiring ...
? If PHP embraces that — note GoLang has something similar — then will comma syntax allow for it with hacky use of parentheses?
$y = switch ($x) {
case 1 => $var = 1,2,3, $var[1],
case -1 => $var = 9,8,7, $var[1],
default => null,
};
That's because
throw
do different thing at the engine level not just jumping things likebreak
,continue
orgoto
.
Are you saying it is literally impossible given the current engine, or just that it has not been done with break
, continue
or goto
?
If
break
is allowed in switch-expressions and its behavior is to break outer loop, don't you think it could be an another inconsistency or confusion? Because I'm 100% sure when people seebreak
withinswitch
they'll all think it should breakswitch
not something else.
No. I would assume the other, so I have to assume I would not be the only one.
you are the one that limit yourself.
Your response makes me think you are responding in anger? I am not arguing in anger myself, so I hope that I am not causing you to reply in anger? I am merely asking posing technical challenges in hopes to ensure that PHP is the best it can be. I assume we all want that?
-MIke
P.S. And I do not mean my input is needed for PHP to be the best it can be but only that having different opinions debated in a reasoned manner tends to make for a better outcome.
Hi Mike
Ok, so let's look at a scenario. Will we be able to do this in the future,
if we decide to allow multiple expressions to result in a final value?
$y = switch ($x) {
case 1 => $arr = get_array(), array_pop($arr),
case -1 => $arr = get_array(), array_unshift($arr),
default => null,
};
As I told you in previous reply, I'm working on block-expressions as well.
So if PHP has block-expressions you can do the same this way:
$y = switch ($x) {
case 1 => eval {
$arr = get_array();
give array_pop($arr);
},
case -1 => eval {
$arr = get_array();
give array_shift($arr);
},
default => null,
};
Which is obviously cleaner and more powerful.
Are you saying it is literally impossible given the current engine, or just
that it has not been done with
break
,continue
orgoto
?
Yes, it is impossible to break out of an ongoing function call without
exceptions.
Your response makes me think you are responding in anger? I am not arguing
in anger myself, so I hope that I am not causing you to reply in anger? I
am merely asking posing technical challenges in hopes to ensure that PHP is
the best it can be. I assume we all want that?
No, I was not angry. Just a little bit tired. I appologize making you feel
that way.
Regards
Ok, so let's look at a scenario. Will we be able to do this in the future, if we decide to allow multiple expressions to result in a final value?
$y = switch ($x) {
case 1 => $arr = get_array(), array_pop($arr),
case -1 => $arr = get_array(), array_unshift($arr),
default => null,
};As I told you in previous reply, I'm working on block-expressions as well. So if PHP has block-expressions you can do the same this way:
$y = switch ($x) {
case 1 => eval {
$arr = get_array();
give array_pop($arr);
},
case -1 => eval {
$arr = get_array();
give array_shift($arr);
},
default => null,
};
I know you said you were working on block expressions, but I did not view that as the best solution to the question I asked although I see that is how you envision it.
Which is obviously cleaner and more powerful.
That is arguable. I find the solution you proposed above that uses eval()
to be very "busy" visually, and needlessly complex/ You could instead simply assume that case statements supports multiple lines with the last providing the value, and thus not requiring the use of an explicit eval()
. But to do that would require semi-colon line terminators, I think.
Note I also think eval()
would also be a nice to have, but what would be even nicer is if the inline switch did not require use of eval()
to support multiple lines per case.
#fwiw
-Mike
Hi Mike
I think your hypothetical PHP syntax won't happen. Not because it's bad
idea but because PHP just simply can't. I understand that you came up with
that idea from Golang background but the reason Golang can do that because
in Golang, assignments are statement not expression. Even ++
or --
are
statement in Golang. While assignments in PHP are expressions so they can
appear in any expression contexts. And it will conflict with other places
as Rowan said in previous reply.
You could instead simply assume that case statements supports multiple
lines with the last providing the value, and thus not requiring the use of
an expliciteval()
.
I'm not fond of implicit things. And I think my proposed solution is more
useful because it can return value anywhere not just at the end. You might
think it is complex or visually busy but others may feel it is more
comfortable if they are able to write well structured and and cleaner codes
by this way.
Regards
On Oct 20, 2019, at 11:11 PM, Kosit Supanyo webdevxp.com@gmail.com
wrote:Ok, so let's look at a scenario. Will we be able to do this in the future,
if we decide to allow multiple expressions to result in a final value?
$y = switch ($x) {
case 1 => $arr = get_array(), array_pop($arr),
case -1 => $arr = get_array(), array_unshift($arr),
default => null,
};As I told you in previous reply, I'm working on block-expressions as well.
So if PHP has block-expressions you can do the same this way:$y = switch ($x) {
case 1 => eval {
$arr = get_array();
give array_pop($arr);
},
case -1 => eval {
$arr = get_array();
give array_shift($arr);
},
default => null,
};I know you said you were working on block expressions, but I did not view
that as the best solution to the question I asked although I see that is
how you envision it.Which is obviously cleaner and more powerful.
That is arguable. I find the solution you proposed above that uses
eval()
to be very "busy" visually, and needlessly complex/ You could
instead simply assume that case statements supports multiple lines with the
last providing the value, and thus not requiring the use of an explicit
eval()
. But to do that would require semi-colon line terminators, I think.Note I also think
eval()
would also be a nice to have, but what would be
even nicer is if the inline switch did not require use ofeval()
to
support multiple lines per case.#fwiw
-Mike
Ok, so let's look at a scenario. Will we be able to do this in the future,
if we decide to allow multiple expressions to result in a final value?
$y = switch ($x) {
case 1 => $arr = get_array(), array_pop($arr),
case -1 => $arr = get_array(), array_unshift($arr),
default => null,
};
Unlike some languages, not every statement in PHP is a valid expression
(e.g. you cannot write "$foo = echo $bar"), so regardless of whether we use
commas or semi-colons, the right-hand-side in those examples would have to
be some new construct, so we could choose whatever syntax we wanted for it.
We'd probably want something other than bare commas anyway, to make it more
widely usable, e.g. in array declarations.
If what you want is a full statement block on the right hand side, then
you'll need to use the current switch statement anyway.
What if in the future PHP implements variadic syntax for assigning to
arrays, e.g. $var = 1,2,3 where $var could satisfy a $variadic parameter
set of parameters w/o requiring...
? If PHP embraces that — note GoLang
has something similar — then will comma syntax allow for it with hacky use
of parentheses?
$y = switch ($x) {
case 1 => $var = 1,2,3, $var[1],
case -1 => $var = 9,8,7, $var[1],
default => null,
};
I'm not really clear what feature you're suggesting here, but I'm pretty
sure it conflicts with existing uses of commas, so shouldn't constrain us
from using them elsewhere.
foo(1,2,3);
foo($var = 1,2,3);
$a = [0, $var = 1,2,3];
// etc
Regards,
Rowan Tommins
[IMSoP]
What if in the future PHP implements variadic syntax for assigning to
arrays, e.g. $var = 1,2,3 where $var could satisfy a $variadic parameter
set of parameters w/o requiring...
? If PHP embraces that — note GoLang
has something similar — then will comma syntax allow for it with hacky use
of parentheses?
$y = switch ($x) {
case 1 => $var = 1,2,3, $var[1],
case -1 => $var = 9,8,7, $var[1],
default => null,
};I'm not really clear what feature you're suggesting here, but I'm pretty
sure it conflicts with existing uses of commas, so shouldn't constrain us
from using them elsewhere.foo(1,2,3);
foo($var = 1,2,3);
$a = [0, $var = 1,2,3];
// etc
You may well be right.
But to follow up to clarify what I was thinking consider the following (hypothetical) PHP syntax. Note that the return value has two comma separated values, and the assignment can accept those multiple values into two comma separated variables in the assignment:
function read_file(string $filepath):string,PHP\Error {
$content = file_get_contents( $filepath );
$error = false === $content
? new PHP\Error( "failed to open '%s'", $filepath )
: null;
return $content, $error;
}
$content, $err = read_file( DIR . '/myfile.txt' );
if (!is_null( $err ) ) {
error_log($err->message);
}
Now yes I know we can do this with arrays and list(), but I have never seen anyone do this in the wild and hence I think it is not idiomatic PHP. And I hypothesize they do not do this because it is ugly to read and write and because PHP will throw a runtime error (vs. load-time error) if you have fewer values returned than you assign:
function read_file( string $filepath ): array {
$content = file_get_contents( $filepath );
$error = false === $content
? new My\Error( "failed to open '%s'", $filepath )
: null;
return array( $content, $error );
}
list( $content, $err ) = read_file( DIR . '/myfile.txt' );
if ( ! is_null( $err ) ) {
error_log( $err->message );
}
With the first example PHP could type check at load time vs. having to wait for coverage of this code at runtime, and :array
type hint in the latter is not as good as the :string, PHP\Error
type hint of the former.
I am probably having unreasonable expectations to believe we will ever see this type of thing in PHP. Still I hold out hold and want to register an objection if I see new syntax that would make this syntax impossible in the future. #fwiw
-Mike
>
> But to follow up to clarify what I was thinking consider the
> following/ (hypothetical) /PHP syntax. Note that the return value
> has two comma separated values, and the assignment can accept
> those multiple values into two comma separated variables in the
> assignment:
>
>
> function read_file(string $filepath):string,PHP\Error {
> $content = file_get_contents( $filepath ); $error = false ===
> $content ? new PHP\Error( "failed to open '%s'", $filepath ) :
> null; return $content, $error; }$content, $err= read_file(
> __DIR__ . '/myfile.txt' ); if (!is_null( $err ) ) {
> error_log($err->message); }
>
Ah, I see. Yes, multiple return values would be nice sometimes.
Note that you can get a lot closer to this in current versions of PHP
than your example, because you can use the [...] syntax for both
creating and destructuring arrays:
function read_file(string $filepath):array {
$content = file_get_contents( $filepath );
$error = false === $content
? new PHP\Error( "failed to open '%s'", $filepath )
: null;
return [ $content, $error ];
}
[ $content, $err ] = read_file( __DIR__ . '/myfile.txt' );
if (!is_null( $err ) ) {
error_log($err->message);
}
> With the first example PHP could type check at load time vs.
> having to wait for coverage of this code at runtime, and `:array`
> type hint in the latter is not as good as the `:string, PHP\Error`
> type hint of the former.
>
The ability to typehint multiple return values like that would be indeed
be useful, but I'm not sure it would need to reserve that syntax. One
way it could be achieved is using a "tuple" declaration, like in Hack:
https://docs.hhvm.com/hack/built-in-types/tuples
In general, grouping things with commas and no parentheses is always
going to *look* ambiguous IMO, even if you can limit where it's used to
not be *technically* ambiguous.
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Hi Kosit,
There's some really interesting ideas in here, thanks for sharing them.
Like function declaration and function expression in JavaScript, if
switch
appears as first token at statement level it will be recognized as
statement but ifswitch
is in expression context it will be
switch-expression.switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};
// Parse error: syntax error, unexpected '=>' (T_DOUBLE_ARROW)But this is OK.
!switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
}; // semicolon is still required because it is an expression
This feels like an odd restriction to me, and one that as far as I'm
aware PHP doesn't have anywhere else. For instance, it might be
considered bad style, but it's possible to use a ternary operator as an
abbreviated if statement:
isset($_GET['logout']) ? $session->logout() : $session->extend();
Was this restriction added to make the implementation easier, or because
you thought it was a useful feature?
You can omit parenthesized expression which is shortcut to
switch (true)
.
This change applies to switch statement as well.$v = switch {
case $x >= 0 && $x <= 100 => 1,
case $x >= 100 && $x <= 200 => 2,
default => 3,
};switch {
case $x >= 0 && $x <= 100:
doSomething1();
break;
case $x >= 100 && $x <= 200:
doSomething2();
break;
default:
doNothing();
break;
}
This is an interesting idea, given that switch(true) is the documented
way to do this right now. However, I've always felt switch(true) was
rather limited in its advantage over if-elseif. I would prefer a syntax
that reduced the boilerplate for:
- Different comparisons applied to the same variable/expression, e.g.
match($user->getScore()) { case <0 => foo(), case >100 => bar(), default
=> baz() } - Different values compared against the same expression using the same
operator, e.g. match( $exception instanceOf ) { case FooException =>
handleFoo(), case BarException => handleBar() }
You can also use
return
andthrow
in result expression. I recalled some
languages have this feature (but I've forgotten what language). This
feature can be very handy and useful in many use cases.$x = 'd';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
case 'c' => return true,
default => throw new Exception("'$x' is not supported"),
};
This seems confusing to me, because it mixes statements and expressions
in a rather unusual way. The "return" case in particular seems ambiguous
- if $v is visible outside the function, will it have a value assigned
to it by the "return" branch, and if so what would that value be? I can
imagine more use cases for the "throw" version, and it's reasonably
obvious how it would behave, so it might be reasonable to have that one
special case.
Additional feature in the demo patch is the 'type guard' unary operator
which is an operator that will perform type check on given value and throw
TypeError
when type mismatch occurred, otherwise return the value as is.
It has the same precedence asnew
.$a = 'This is a string';
$v = <int>$a; // TypeError: Value is expected to be int, string given
This is a very interesting feature, although I think what would be even
more useful would be syntax to check if something can be cast, i.e.
the same check you have here, but as an operator evaluating to boolean.
That way, you could write code like this:
if ( <int>$_GET['id'] ) {
$user = getUser($_GET['id']):
}
else {
echo "Invalid ID provided";
}
Which would be equivalent (given a type hint on getUser() and no
strict_types declaration) to this, but without needing to use exceptions
as flow control:
try {
getUser($_GET['id']);
}
catch ( TypeError $e ) {
echo "Invalid ID provided";
}
Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Hi Rowan
Thank you for your reply.
Was this restriction added to make the implementation easier, or because
you thought it was a useful feature?
This is not a restriction but a result of implementation workarounds. Let's
see:
switch ($x) {
case 1:
echo "ONE\n";
break;
case 2:
echo "TWO\n";
break;
}
vs
switch ($x) {
case 1 => print "ONE\n",
case 2 => print "TWO\n",
};
You can recognize the difference of those by looking for =>
right? But
the parser generator (bison) cannot do that in LR mode (it maybe can in GLR
mode but I'm sure that would be unacceptable due to performance losses/more
memory usage). So workarounds have to be used. I did it by simply return
different tokens from the lexer base on previous token before switch
.
https://github.com/webdevxp/php-src/blob/switch-expression/Zend/zend_language_scanner.l#L1460
https://github.com/webdevxp/php-src/blob/switch-expression/Zend/zend_language_scanner.l#L1247
And use that different tokens to define rules in the parser definition so
it won't have any shift/reduce conflicts.
https://github.com/webdevxp/php-src/blob/switch-expression/Zend/zend_language_parser.y#L443
https://github.com/webdevxp/php-src/blob/switch-expression/Zend/zend_language_parser.y#L1107
- Different comparisons applied to the same variable/expression, e.g.
match($user->getScore()) { case <0 => foo(), case >100 => bar(), default
=> baz() }
I'd thought about that feature too. But since I also introduced type guard
operator which uses <
token it would have parser conflicts and simple
workarounds cannot apply.
$v = switch ($x) {
case < <int>$y => 1, // shift/reduce conflict here
};
But if type guard was removed or changed to use another token like (:int)
that feature would be easy to implement because currently ZEND_CASE
opcode has extended_value
unused so we can use it to store operator data.
Cheers
On Sun, Oct 20, 2019 at 9:15 PM Rowan Tommins rowan.collins@gmail.com
wrote:
Hi Kosit,
There's some really interesting ideas in here, thanks for sharing them.
Like function declaration and function expression in JavaScript, if
switch
appears as first token at statement level it will be recognized
as
statement but ifswitch
is in expression context it will be
switch-expression.switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};
// Parse error: syntax error, unexpected '=>' (T_DOUBLE_ARROW)But this is OK.
!switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
}; // semicolon is still required because it is an expressionThis feels like an odd restriction to me, and one that as far as I'm
aware PHP doesn't have anywhere else. For instance, it might be
considered bad style, but it's possible to use a ternary operator as an
abbreviated if statement:isset($_GET['logout']) ? $session->logout() : $session->extend();
Was this restriction added to make the implementation easier, or because
you thought it was a useful feature?You can omit parenthesized expression which is shortcut to
switch (true)
.
This change applies to switch statement as well.$v = switch {
case $x >= 0 && $x <= 100 => 1,
case $x >= 100 && $x <= 200 => 2,
default => 3,
};switch {
case $x >= 0 && $x <= 100:
doSomething1();
break;
case $x >= 100 && $x <= 200:
doSomething2();
break;
default:
doNothing();
break;
}This is an interesting idea, given that switch(true) is the documented
way to do this right now. However, I've always felt switch(true) was
rather limited in its advantage over if-elseif. I would prefer a syntax
that reduced the boilerplate for:
- Different comparisons applied to the same variable/expression, e.g.
match($user->getScore()) { case <0 => foo(), case >100 => bar(), default
=> baz() }- Different values compared against the same expression using the same
operator, e.g. match( $exception instanceOf ) { case FooException =>
handleFoo(), case BarException => handleBar() }You can also use
return
andthrow
in result expression. I recalled
some
languages have this feature (but I've forgotten what language). This
feature can be very handy and useful in many use cases.$x = 'd';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
case 'c' => return true,
default => throw new Exception("'$x' is not supported"),
};This seems confusing to me, because it mixes statements and expressions
in a rather unusual way. The "return" case in particular seems ambiguous
- if $v is visible outside the function, will it have a value assigned
to it by the "return" branch, and if so what would that value be? I can
imagine more use cases for the "throw" version, and it's reasonably
obvious how it would behave, so it might be reasonable to have that one
special case.Additional feature in the demo patch is the 'type guard' unary operator
which is an operator that will perform type check on given value and
throw
TypeError
when type mismatch occurred, otherwise return the value as
is.
It has the same precedence asnew
.$a = 'This is a string';
$v = <int>$a; // TypeError: Value is expected to be int, string givenThis is a very interesting feature, although I think what would be even
more useful would be syntax to check if something can be cast, i.e.
the same check you have here, but as an operator evaluating to boolean.That way, you could write code like this:
if ( <int>$_GET['id'] ) {
$user = getUser($_GET['id']):
}
else {
echo "Invalid ID provided";
}Which would be equivalent (given a type hint on getUser() and no
strict_types declaration) to this, but without needing to use exceptions
as flow control:try {
getUser($_GET['id']);
}
catch ( TypeError $e ) {
echo "Invalid ID provided";
}Regards,
--
Rowan Tommins (né Collins)
[IMSoP]
Hi Kosit,
You can recognize the difference of those by looking for
=>
right? But
the parser generator (bison) cannot do that in LR mode (it maybe can in GLR
mode but I'm sure that would be unacceptable due to performance losses/more
memory usage). So workarounds have to be used. I did it by simply return
different tokens from the lexer base on previous token beforeswitch
.
Thanks for clarifying, I thought it might be something like that.
- Different comparisons applied to the same variable/expression, e.g.
match($user->getScore()) { case <0 => foo(), case >100 => bar(), default
=> baz() }I'd thought about that feature too. But since I also introduced type guard
operator which uses<
token it would have parser conflicts and simple
workarounds cannot apply.
I'm not sure <foo> is the right syntax for type guards, but it's possible
we'd want a different syntax for new switch/match functionality anyway. For
instance, using a placeholder for the value being tested, like $$ (a common
choice in other languages is _, but that's a valid and commonly used
function name):
match ( $user->getScore() ) {
$$ < 0, $$ > 100 => foo(),
$$ < 10 => bar(),
default => baz()
}
You could also allow the placeholder in the opening clause to define the
same operation for each case:
match ( $user->getScore() < $$ ) {
0, default => foo(),
10 => bar(),
100 => baz()
}
Regards,
Rowan Tommins
[IMSoP]
Hi Rowan
I'm not sure <foo> is the right syntax for type guards, but it's possible
we'd want a different syntax for new switch/match functionality anyway.
I've decided to change the syntax to (: foo)
. It may look weird at frist
but it is able explain why I think this syntax is more appropriate. Because
PHP already uses :
to specify return type of functions so (: foo)
syntax can be seen as a way to specifying return type of expressions. And
it can be put into the same visual category of cast operators.
$a = (int)$b; // filter
$c = (: int)$d; // guard
This syntax choice may not satisfy everyone's taste but at least it is
explainable. So now there's 2 choices <foo>
and (: foo)
. And more
choices are welcome from anyone who think type guard should be a new
feature.
For instance, using a placeholder for the value being tested, like $$ (a
common
choice in other languages is _, but that's a valid and commonly used
function name):
About that, I'm working on a different approach which I would like to show
you after I finished the next demo :)
Regards
On Mon, Oct 21, 2019 at 8:52 PM Rowan Tommins rowan.collins@gmail.com
wrote:
Hi Kosit,
On Sun, 20 Oct 2019 at 16:20, Kosit Supanyo webdevxp.com@gmail.com
wrote:You can recognize the difference of those by looking for
=>
right? But
the parser generator (bison) cannot do that in LR mode (it maybe can in
GLR
mode but I'm sure that would be unacceptable due to performance
losses/more
memory usage). So workarounds have to be used. I did it by simply return
different tokens from the lexer base on previous token beforeswitch
.Thanks for clarifying, I thought it might be something like that.
- Different comparisons applied to the same variable/expression, e.g.
match($user->getScore()) { case <0 => foo(), case >100 => bar(), default
=> baz() }I'd thought about that feature too. But since I also introduced type
guard
operator which uses<
token it would have parser conflicts and simple
workarounds cannot apply.I'm not sure <foo> is the right syntax for type guards, but it's possible
we'd want a different syntax for new switch/match functionality anyway. For
instance, using a placeholder for the value being tested, like $$ (a common
choice in other languages is _, but that's a valid and commonly used
function name):match ( $user->getScore() ) {
$$ < 0, $$ > 100 => foo(),
$$ < 10 => bar(),
default => baz()
}You could also allow the placeholder in the opening clause to define the
same operation for each case:match ( $user->getScore() < $$ ) {
0, default => foo(),
10 => bar(),
100 => baz()
}Regards,
Rowan Tommins
[IMSoP]
Like function declaration and function expression in JavaScript, if
switch
appears as first token at statement level it will be recognized as
statement but ifswitch
is in expression context it will be
switch-expression.switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
};
// Parse error: syntax error, unexpected '=>' (T_DOUBLE_ARROW)But this is OK.
!switch ($expr) {
case $cond1 => $result1,
case $cond2 => $result2,
case $cond3 => $result3,
default => $default_result,
}; // semicolon is still required because it is an expressionThis feels like an odd restriction to me, and one that as far as I'm aware PHP doesn't have anywhere else. For instance, it might be considered bad style, but it's possible to use a ternary operator as an abbreviated if statement:
isset($_GET['logout']) ? $session->logout() : $session->extend();
Was this restriction added to make the implementation easier, or because you thought it was a useful feature?
What restriction are you referring to? The forced default
or the semi-colon being required?
If default
, I concur.
I've always felt switch(true) was rather limited in its advantage over if-elseif.
I would much prefer to use a switch for multiple, mutually exclusive cases no matter what how complex the expression is because with a switch the cases expressions line up vertically. With if
and else if
it is harder to vertically eyeball the different mutually exclusive cases.
With that, I would even argue that we deprecate else
. Except I know that would be a battle so I wouldn't actually suggest it. :-)
I would prefer a syntax that reduced the boilerplate for:
- Different comparisons applied to the same variable/expression, e.g. match($user->getScore()) { case <0 => foo(), case >100 => bar(), default => baz() }
- Different values compared against the same expression using the same operator, e.g. match( $exception instanceOf ) { case FooException => handleFoo(), case BarException => handleBar() }
However, I would very much like to see both of those options, for switch expressions and switch statements.
It is "syntax sugar," but IMO[1] programming languages evolve best when they recognize commonly used-patterns and then apply syntax sugar to make those patterns more obvious, less error prone and easier to reason about.
Further, your introduction of a match
keyword here is intriguing, and we could probably leverage it for other use cases. Maybe as an alternate to switch statements that do not require break
s?
$x = 'd';
$v = switch ($x) {
case 'a' => 1,
case 'b' => 2,
case 'c' => return true,
default => throw new Exception("'$x' is not supported"),
};The "return" case in particular seems ambiguous - if $v is visible outside the function, will it have a value assigned to it by the "return" branch, and if so what would that value be?
It should have its prior value, since the expression did not complete.
Alternately we could choose to always assign it null on return or break.
But how often will we have this use-case? Only for global variables, which hopefully will eventually become extinct in userland code.
This is a very interesting feature, although I think what would be even more useful would be syntax to check if something can be cast, i.e. the same check you have here, but as an operator evaluating to boolean.
That way, you could write code like this:
if ( <int>$_GET['id'] ) {
$user = getUser($_GET['id']):
}
else {
echo "Invalid ID provided";
}Which would be equivalent (given a type hint on getUser() and no strict_types declaration) to this, but without needing to use exceptions as flow control:
try {
getUser($_GET['id']);
}
catch ( TypeError $e ) {
echo "Invalid ID provided";
}
+1 to use for can-cast checking. In GoLang this is called a type assertion and is thus similar:
if id,ok = get['id'].(string); ok {
$user = get['id'].(string);
} else {
echo "Invalid ID provided";
}
It would be super nice for PHP to have an equivalent.
That said, we should almost be able to do both, and just let PHP throw an error if there is a problem. This would be useful if you know 100% that the code won't fail because of the code that came before it:
if ( is_numeric($_GET['id'] ?? 0 ) ) {
$id = intvar($_GET['id'] ?? 0);
$user = getUser(<int>$id):
}
-Mike
[1] https://mikeschinkel.me/2019/in-defense-of-syntactic-sugar/ <https://mikeschinkel.me/2019/in-defense-of-syntactic-sugar/
What restriction are you referring to? The forced
default
or the
semi-colon being required?
Neither; I was referring to not being able to put a switch expression as a
statement on its own, which Kosit has explained is a limitation of the
parser.
I would much prefer to use a switch for multiple, mutually exclusive cases
no matter what how complex the expression is because with a switch the
cases expressions line up vertically. Withif
andelse if
it is harder
to vertically eyeball the different mutually exclusive cases.
I've never particularly been bothered by it, but wouldn't adding four
spaces in your "if" statement make it line up with all your "elseif"
statements?
The "return" case in particular seems ambiguous - if $v is visible outside
the function, will it have a value assigned to it by the "return" branch,
and if so what would that value be?It should have its prior value, since the expression did not complete.
Alternately we could choose to always assign it null on return or break.
But how often will we have this use-case? Only for global variables,
which hopefully will eventually become extinct in userland code.
Not just global variables, no; it could be assigning an object property
($this->foo = switch($x){ case 1 => return 1; }) for instance. It could
also be in the argument for a function call ( doSomething(switch($x){ case
1 => return 1; }) ) in which case presumably the function would not be
called at all? Overall, having a "return" in the middle of an expression
would be very confusing, I think.
That said, we should almost be able to do both, and just let PHP throw
an error if there is a problem. This would be useful if you know 100% that
the code won't fail because of the code that came before it:if ( is_numeric($_GET['id'] ?? 0 ) ) {
$id = intvar($_GET['id'] ?? 0);$user = getUser(<int>$id):
}
I'm not sure what the point of this example is. You've just cast to int, so
the assertion can never fail, and if getUser has a type hint, it's about to
make that assertion anyway.
Regards,
Rowan Tommins
[IMSoP]
I would much prefer to use a switch for multiple, mutually exclusive cases
no matter what how complex the expression is because with a switch the
cases expressions line up vertically. Withif
andelse if
it is harder
to vertically eyeball the different mutually exclusive cases.I've never particularly been bothered by it, but wouldn't adding four
spaces in your "if" statement make it line up with all your "elseif"
statements?
Possibly, but then again I am:
- Mostly reading other people's code who will not have formatted their IFs that way, and
2.) Using a code reformatter to standardize code format via my IDE and there is no option for that.
As for #1, I know I cannot enforce switch usage outside my own team, but at least for own team our standard says "use switch for multiple expressions" when possible so at least it would provide we can have consistency across our team.
But how often will we have this use-case? Only for global variables,
which hopefully will eventually become extinct in userland code.Not just global variables, no;...
Fair point.
That said, we should almost be able to do both, and just let PHP throw
an error if there is a problem. This would be useful if you know 100% that
the code won't fail because of the code that came before it:if ( is_numeric($_GET['id'] ?? 0 ) ) {
$id = intvar($_GET['id'] ?? 0);
$user = getUser(<int>$id):
}I'm not sure what the point of this example is. You've just cast to int, so
the assertion can never fail, and if getUser has a type hint, it's about to
make that assertion anyway.
Yeah, I know. I could not come up with a great example on the spot.
I'll have to go off and do some more coding because that's when I always come across the good examples. Which is usually not the same time I need the good example. :-(
-Mike