Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107589 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 76555 invoked from network); 20 Oct 2019 17:36:43 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by 76.75.200.58 with SMTP; 20 Oct 2019 17:36:43 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id 294B12C0463 for ; Sun, 20 Oct 2019 08:20:10 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp3.php.net X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS3215 2.6.0.0/16 X-Spam-Virus: No Received: from mail-oi1-x236.google.com (mail-oi1-x236.google.com [IPv6:2607:f8b0:4864:20::236]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp3.php.net (Postfix) with ESMTPS for ; Sun, 20 Oct 2019 08:20:09 -0700 (PDT) Received: by mail-oi1-x236.google.com with SMTP id i16so9033120oie.4 for ; Sun, 20 Oct 2019 08:20:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=/MrxZvz8rvOWQ6CUX5ZgGfLc8rS6EQbReoWjGxjivxE=; b=LhVxr/UcEWpNuetHJAwIKCGlQ1Dwqv9Tjau9IjKP6UUAUr1QglXyE2NzRkVTDAOezM kdaSKCWqD1fgil10K5vhkOAIaw681ihYKSjy5mzBUC07zva4Ny0R4oleeRtym9PPLBfR R5imcumsFdqG7dwYgVrMN7m6Jr8U2S4pDXb+j2CL8V/xoR8/szqgcGE+mp508hI4Rkqj //O2uI91SKAyCVVJQOPS31w8fPAbPR3dZ3Zc0WUdbu7ZD0itW0U9KN6vq+USv7WOiRdY r9wPR316jI55Jeb+wuzbCl9I/xGuY0WZj6a4uJatnGV+vbwtj+syDasR5M5B7ya5+KGs tQuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=/MrxZvz8rvOWQ6CUX5ZgGfLc8rS6EQbReoWjGxjivxE=; b=JUaQOnWL/pLxebRnnWELGFI5LncV/BgXCgP6ykPkYeeeXsMCZ7G8H9YOJ9hjSEeytJ UBpnNX/mcqtCgWlyc1ZXY7WUSyGW4prM/0wFjTlX5jqNpQFoPYfhW7WrKF9yfj0BxSK1 zM+zaLrh+RSJwz+La4ldmKXiAUPjJze+0GAbOxrrQvZZ2n5kflBSvz84/VtsXnWRShGk alx5gYrPHrGvMsduQ+W57xMrwF5FdWBxAtEAqVXhqtmX6jIT9s5hZjJ4y76I1y74l/0M Fnd/e3IGz+tl11fob7m5md35ZMbxiocL1gBC3ttzFEbkH66bo0gRB07htAx4eJweh6vz XpWQ== X-Gm-Message-State: APjAAAVwN+Kg48WxOCZRce/9G33lzgGH6swWtP+jrwaSgS9ZGz3vsueK yODqyF7N1N+btlj0R7aLRJz44sVOtQVMEIGXjcE= X-Google-Smtp-Source: APXvYqxaVBt4osszg5ehSLJFJe4qOGr29xAvDOC6MrrlyB9QL3WTByI0cYw3inxDPOwjV0NkF1ayQa8hSD3fNdtW+SU= X-Received: by 2002:a54:4f87:: with SMTP id g7mr15790767oiy.100.1571584808674; Sun, 20 Oct 2019 08:20:08 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Sun, 20 Oct 2019 22:19:56 +0700 Message-ID: To: Rowan Tommins Cc: PHP internals Content-Type: multipart/alternative; boundary="0000000000004169ff0595591abc" X-Envelope-From: Subject: Re: [PHP-DEV] 'switch-expression' and the 'type guard' unary operator demo From: webdevxp.com@gmail.com (Kosit Supanyo) --0000000000004169ff0595591abc Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable 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 =3D> print "ONE\n", case 2 =3D> print "TWO\n", }; You can recognize the difference of those by looking for `=3D>` 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_langua= ge_scanner.l#L1460 https://github.com/webdevxp/php-src/blob/switch-expression/Zend/zend_langua= ge_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_langua= ge_parser.y#L443 https://github.com/webdevxp/php-src/blob/switch-expression/Zend/zend_langua= ge_parser.y#L1107 * Different comparisons applied to the same variable/expression, e.g. > match($user->getScore()) { case <0 =3D> foo(), case >100 =3D> bar(), defa= ult > =3D> 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 =3D switch ($x) { case < $y =3D> 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 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 recognize= d > as > > statement but if `switch` is in expression context it will be > > switch-expression. > > > > switch ($expr) { > > case $cond1 =3D> $result1, > > case $cond2 =3D> $result2, > > case $cond3 =3D> $result3, > > default =3D> $default_result, > > }; > > // Parse error: syntax error, unexpected '=3D>' (T_DOUBLE_ARROW) > > > > But this is OK. > > > > !switch ($expr) { > > case $cond1 =3D> $result1, > > case $cond2 =3D> $result2, > > case $cond3 =3D> $result3, > > default =3D> $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 =3D switch { > > case $x >=3D 0 && $x <=3D 100 =3D> 1, > > case $x >=3D 100 && $x <=3D 200 =3D> 2, > > default =3D> 3, > > }; > > > > switch { > > case $x >=3D 0 && $x <=3D 100: > > doSomething1(); > > break; > > case $x >=3D 100 && $x <=3D 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 =3D> foo(), case >100 =3D> bar(), defa= ult > =3D> baz() } > * Different values compared against the same expression using the same > operator, e.g. match( $exception instanceOf ) { case FooException =3D> > handleFoo(), case BarException =3D> 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 =3D 'd'; > > $v =3D switch ($x) { > > case 'a' =3D> 1, > > case 'b' =3D> 2, > > case 'c' =3D> return true, > > default =3D> 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 =3D 'This is a string'; > > $v =3D $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 ( $_GET['id'] ) { > $user =3D 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=C3=A9 Collins) > [IMSoP] > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --0000000000004169ff0595591abc--