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.coll...@gmail.com>
wrote:

> 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]
>
> --
> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>

Reply via email to