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

From: Date: Sun, 20 Oct 2019 11:59:32 +0000
Subject: Re: 'switch-expression' and the 'type guard' unary operator demo
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-107588@lists.php.net to get a copy of this message
Hi Kosit, There's some really interesting ideas in here, thanks for sharing them. 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?
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 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"),
};
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 as new. $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]

« previous php.internals (#107588) next »