Re: 'switch-expression' and the 'type guard' unary operator demo
| From: | Mike Schinkel | Date: | Sun, 20 Oct 2019 21:35:21 +0000 |
| Subject: | Re: 'switch-expression' and the 'type guard' unary operator demo | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-107599@lists.php.net to get a copy of this message | ||
> On Oct 20, 2019, at 7:59 AM, Rowan Tommins <rowan.collins@gmail.com> wrote:
> On 19/10/2019 17:40, Kosit Supanyo wrote:
>> 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
>
>
> 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?
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
breaks?
>> $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/>