Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:109947 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 33206 invoked from network); 30 Apr 2020 13:44:39 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 30 Apr 2020 13:44:39 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 042271804C2 for ; Thu, 30 Apr 2020 05:18:19 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.7 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-yb1-f172.google.com (mail-yb1-f172.google.com [209.85.219.172]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 30 Apr 2020 05:18:18 -0700 (PDT) Received: by mail-yb1-f172.google.com with SMTP id t18so3092806ybp.2 for ; Thu, 30 Apr 2020 05:18:18 -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; bh=hcBGLm3sG5AbZUF4e5AV1IxtytXNovdclbCGdqgSrKM=; b=Xu1wO2a6n+aJhNc3wvtCnZPUhGC2gXcOW5wip3+UNuW6E4auDUcgb2SKxBrBVjk1z2 qS36zzYGg5qc4qxIlAy8Q/7UNwd5hdxlSBhIJg3L2snBiZ05zLGH2lajs6HoZ3oXEGAf Az4iBj9H6CwumC+2BdIwprudSheH56IjFNR0Rd9/KDkJgYmAJ3MEBaSjSl3e520GpbOF ycyn28LjcO+rahbgoEp4dM3aFeLeAuwXcSQOw2sgptP30PUSklaYD0vm3M/5uALTdU+7 z5yi02EBscKjllAzF+D+iPhbCrVpsL5ziM6nn/7eakUO0UEeCYAmmmFV+/4NdS5YQCaV BOTw== 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; bh=hcBGLm3sG5AbZUF4e5AV1IxtytXNovdclbCGdqgSrKM=; b=exKcNn9haCpHoEAh7fdD6Xpxas5X3CbMOQQx/WxlcC1WqvVUFBLOcMk7Fl4Uct/Tl1 hoxGOvgrdmQhvjtlB8ZGffvt30PQSEWJ6SoF9SflCd/jneUO/7TvvIj152lqM+Q3Nyco J5tUYcjVapHj1/h42QKA7ksdwr91uaSHPmeK5hkqTx37HjQSFXQnOkpzKIjWeXSV/34S m5KeHLCKGDq9XFyMY23jvB1phzrP4lmck1MM7659S+dcSPz/SmqBL8+nefqj3GPsT3/z ICe8Mu0OVOPr2+IaTmdsqzpjUSmw+Nice7TbOOyF/ZxWby1w9Se58oNysRhRlFBqkNmg 1iQg== X-Gm-Message-State: AGi0PuasVYxbzvV5Cy/Mym9bClTpM1Jtmm162MuF1EroDxFpR2rXW+Wa QONI/rK0iy2ZJ/XWzzq9l0WTkzZOkK+kW1P0Dez/6aUP X-Google-Smtp-Source: APiQypLXaUDmmN++9ezd/FGy7zZxE3lYHfEul3mBNmGSGfnB1dQSmp7lJNAo+z7UbMPgtMOAEtveJStd3tiVNMOOets= X-Received: by 2002:a25:c697:: with SMTP id k145mr4931711ybf.2.1588249096517; Thu, 30 Apr 2020 05:18:16 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 30 Apr 2020 14:18:05 +0200 Message-ID: To: php internals Content-Type: text/plain; charset="UTF-8" Subject: Re: [PHP-DEV] [VOTE] match expression From: tovilo.ilija@gmail.com (Ilija Tovilo) Hi someniatko I think you have a firm grasp of the key issues but I don't agree with your conclusion. > Problem no. 2 could be addressed by > allowing "complex" expressions consisting of, potentially, few > statements, language-wide, solving the issue both for short closures > and for `match` I have analysed this approach a while back and I don't think there is a universal and elegant solution. I have mentioned this in the list but never fully explained it. Upfront, when I say "block expression" I'm talking about a block `{}` that contains any number of statements with some terminating expression that will be returned. $x = { foo(); bar(); <= baz(); }; // Result of baz() is now assigned to $x There are three potential use cases for language wide block expressions. 1. Match expressions 2. Arrow functions 3. Everything else The problem is that they all have slightly different semantics. 1. A block expression in a match arm would require a return value depending if the outer match return value is used match ($x) { 1 => {}, // Doesn't require a return value } $y = match ($x) { 1 => {}, // Error, this does require a return value }; 2. Arrow functions would only require a return value if the function has an explicit return type (to be consistent with functions and normal closures) $x = fn() => {}; // This is fine, the function returns null $x = fn(): ?int => {}; // Uncaught TypeError: Return value of {closure}() must be of the type int or null, none returned It's very questionable whether we even want to allow block-style return values in arrow functions. $x = fn() => { foo(); bar(); <= baz(); // Why should we allow this? You can just use return }; 3. For every other expression the return value of the block would always be required // All of these are errors, return value is required $x = {}; foo({}); {} + 1; // etc. It's also highly questionable whether use case 3 is actually very useful at all because PHP doesn't have block scoping and all the inner variables will leak out into the outer scope. The only potential improvement here is readability. $this->foo = { $bar = new Bar(); $foo = new Foo(); $foo->bar = $bar; <= $foo; // Or whatever block syntax }; // $bar still exists here but it's a little // more obvious it shouldn't be used anymore No matter if statement blocks become a language wide feature or not, we won't get around handling these cases slightly differently. An additional complication is that blocks already exist as "statement list" statements: https://github.com/php/php-src/blob/php-7.4.5/Zend/zend_language_parser.y#L427 function foo() { { // This is a "statement list" statement } if (true) { // This is also a "statement list" statement } } Whether we'd also (a) convert these to block expressions or (b) keep "statement list" statements and block expressions separate is unclear. If we do convert "statement list" statements to block expressions we'll have to make the semicolon of a statement level block expression optional to avoid BC breaks. I received a lot of criticism for the same thing in this RFC. If we don't convert "statement list" statements to block expressions empty blocks won't become valid syntax in match arms and we'll have to explicitly allow them in the grammar (which is what this RFC is doing right now). (a) { // Blocks are expressions now, expressions at a statement level require a semicolon. // The semicolon must stay optional or we'll have a BC break. } (b) { // This is still a statement } match ($x) { 1 => { // Also a statement which means it can't be used here unless we explicitly allow expressions AND "statement list" statements }, } To summarize, blocks are only really useful in match arms and arrow functions and behave differently even in just these two cases. While I wouldn't mind language wide blocks it isn't the universally elegant solution people make it up to be. This is the best explanation I could give. Let me know if it's still not completely clear. > IMO this covers vast majority of use-cases of the PHP statements. Not really, if you look at Nikita's analysis of 50 random switch statements. It only covers ~40%. > IMO a good language should enforce better quality. The main issue I have with this is that good code quality doesn't look the same to everybody. Even programmers with decades of experience will disagree on fundamentals. I'd be very hesitant to say that moving two lines into a function is an improvement, especially if it's only called once. The only thing it does is disrupt the reading flow. match ($x) { 1 => { foo(); bar(); }, } vs. match ($x) { 1 => fooAndBar(), }; function fooAndBar() { foo(); bar(); } I don't think using a closure for all of these cases is a viable solution. > Also, those people who would benefit > from less boilerplate which match in expression-only form provides, > are usually the people who care about code quality. It's not that black and white. I work in a lot of legacy projects that could benefit from match expressions but it's simply not realistic to refactor every single switch statement that contains more than a one liner. Also, I don't always care about code quality the same. If I write a throwaway script I wouldn't care if the match arm contains 20 lines but the safety of the match would be useful nonetheless. Ilija