> On Oct 20, 2019, at 7:59 AM, Rowan Tommins <rowan.coll...@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 `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/>