Re: 'switch-expression' and the 'type guard' unary operator demo

From: 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/>

« previous php.internals (#107599) next »