Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:120661 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 63592 invoked from network); 22 Jun 2023 12:35:21 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 22 Jun 2023 12:35:21 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 3AD5F180511 for ; Thu, 22 Jun 2023 05:35:20 -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.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,FUZZY_PRICES,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE 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-f177.google.com (mail-yb1-f177.google.com [209.85.219.177]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature ECDSA (P-256) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 22 Jun 2023 05:35:19 -0700 (PDT) Received: by mail-yb1-f177.google.com with SMTP id 3f1490d57ef6-bd77424c886so7046632276.0 for ; Thu, 22 Jun 2023 05:35:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech-net.20221208.gappssmtp.com; s=20221208; t=1687437319; x=1690029319; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=c1TTnGbVdqpSkV7YeOxcc0JMEPXPvZQX0LZ1YFN13UA=; b=1DMS6hTMvqIZCt9lK7YVO4wKd3cDtEiSOhLO5+tZg5azlpo21rVjECap4XN+ePEped YLWV6NWSmDjAiipjRDsMGeAnNGsSX7VfvilJcA/KlWnQgA+i8L7JfP6IUIVe6J6LOaSm fVq70Cy2YorSU5CSvZxSBwUiORl+jir9rvSv1Q7wWIKpRaIYGGxgBrQ0p+qDBZEXhe4O FgoGcW2NG1fI9+tHS/w9Buef1ufDTPA2Q1qABh+QN9lQ5lFOTkrEC79Ewy5rj+fB3fas eLpB2ReQI0E68m4FiuP5VyCWmYw5RCiUw+/1vbWnaliO7VBs7yVF2InVVcJ6ipGyOopz ewiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687437319; x=1690029319; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=c1TTnGbVdqpSkV7YeOxcc0JMEPXPvZQX0LZ1YFN13UA=; b=DRRhdhk7pSmypJWKIDqDQmBdTzf4O5M9h5VYmchJ0jw155wrZBb0IfAVpslslgauZ5 cFzYw1ZpprE3t9LOwb8DJgdcOx9YghoGsITHTEsk3+wV/oaPI+jnkFoSKjfBdJCgIp+d +7IHaCtVXtJzRlmD3ALF8WbfzfYOsI0TvOzLHdxp+6L9c0aFeOyFF7BcC5IUD+qX2OBl tpFsILvP5KZ24Lbd4eagatHGsJqVakmGmsyJ+lsX4rfDyC3Vg5IGqa83AXWJge/+8wQr BoaezkvYueXYDojvU7sQvZ/oTc1Hw/hYnVI1Nabr+1oDDaiil9G0EgvJ3F35Zrln/735 cNAw== X-Gm-Message-State: AC+VfDyFdXdnN1rH47sIRAOa7Uu2km5nP1+x+/12+1Ece/ATGk5h+yrn fyQlqne+lHLpt0+VBXNqJU8EiVgg0+8PqlS9F20WAw== X-Google-Smtp-Source: ACHHUZ6L3irl1aEFqxbOJlQgskCA7WobxMS0z7BZIWKj1gy6cIPsNKbxhdpxCzt6tqGKHgBKTJ1UduATBCnRy7LQTQw= X-Received: by 2002:a25:2f84:0:b0:bb1:684a:c369 with SMTP id v126-20020a252f84000000b00bb1684ac369mr14864881ybv.54.1687437319081; Thu, 22 Jun 2023 05:35:19 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 22 Jun 2023 14:35:08 +0200 Message-ID: To: Ilija Tovilo Cc: PHP internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Expression code blocks From: andreas@dqxtech.net (Andreas Hennings) Hello Ilija, On Sat, 17 Jun 2023 at 13:27, Ilija Tovilo wrote: > > Hi Andreas > > On Fri, Jun 16, 2023 at 9:23=E2=80=AFPM Andreas Hennings wrote: > > > > Hello list, > > I don't know if something like this was already proposed in the past, > > I did not find anything. > > > > Sometimes it would be nice to have a code block inside an expression, l= ike this: > > > > public function f(string $key) { > > return $this->cache[$key] ??=3D { > > // Calculate a value for $key. > > [...] > > return $value; > > } > > } > > This has been discussed a few years back when match expressions were > proposed. I originally wanted to include support for code blocks along > with expressions to offer a more complete alternative to switch > statements. The other major use-case for block expressions are arrow > functions. > > Unfortunately, a general solution seems suboptimal due to the subtle > semantic differences. See this message for my detailed thought > process. > > https://externals.io/message/109941#109947 I looked at this and I think all of this can be solved. About ambiguity with existing code blocks: I think we should prepend a new keyword like "expr". I was a bit hesitant to introduce a new keyword, but I think it is the cleanest solution. So it becomes `$x =3D expr {return 5;};`. For the return syntax, let's simply use `return`, instead of a new language construct like "<=3D". About return being required or not: Let's do the same as in a regular function body: - If return value is omitted, an implicit NULL is returned. - IDEs / static analysis can look at the wider context and complain if it thinks a return value should be added or omitted. PHP can simply ignore the problem. This means that the following will be true: assert(NULL =3D=3D=3D expr {}); assert(NULL =3D=3D=3D expr {return;}); assert(NULL =3D=3D=3D expr {return NULL;}); > > I believe it would be best to address blocks for match arms and arrow > functions separately. The new syntax could be used in all 3 cases: $y =3D match ($x) { 1 =3D> expr {}, // IDE complains, but PHP does not care. } $fn =3D fn () =3D> expr {}; // Totally ok. $fn =3D fn (): array =3D> expr {}; // Error at runtime when it is executed. $fn =3D fn (bool $arg): array =3D> expr {if ($arg) return [];}; // Possible error at runtime when it is executed. We could also declare a return type on the expr, like so: But I don't see a big advantage. $x =3D expr: array {return [];}; We can also use generators: $generator =3D expr {yield 'A'; yield 'B';}; assert($generator instanceof \Generator); > > I don't believe blocks for general expressions are that useful in PHP > due to the lack of block scoping. We could introduce block scoping for that specific language feature. And even without it, to me these expression blocks would still be useful. If the default is "use all by ref, leak all", it would feel more like other language constructs like foreach(), if/else etc. If the default is "capture all by value", it would feel more like a short closure. Perhaps there could be something to control the capturing type. E.g. a `use` part? expr {..} // Capture all by value, don't leak inner variables. expr use () {..} // Capture nothing. expr use ($x) {..} // Capture specific variable $x, by value. expr use (*) {..} // Capture all by value, don't leak. expr use (&*) {..} // Capture all by reference, leak all? Or we could have different keywords: expr {..} // Capture all by reference, leak all inner variables. scope {..} // Capture all by value, don't leak. I personally think mostly of uses cases with cache and ??=3D. These exist a lot within Drupal, both with runtime cache and persistent cac= he. With the expression block these could be shortened like this: $x =3D $runtime_cache['x'] ??=3D $persistent_cache['x'] ??=3D { ... // Calculate value for x. return $value; }; I could also see it used in generated code that behaves as a huge nested expression. > Your suggestion to make the block a > separate closure could avoid that (as well as the optimizer issue > mentioned below) but comes with new issues, like making modification > of captured values impossible without by-ref capturing. It seems > confusing that fn {} is auto-executed while fn() {} isn't, as the > former looks like a shortened version of the latter. fn() =3D> fn {} > would also look quite weird. match ($x) { 1 =3D> fn {} } seems ok, > except for being somewhat lengthy. > > On another note, the vote for blocks in short closures has failed > lately (https://wiki.php.net/rfc/auto-capture-closure). > > The message above also addresses the syntax ambiguity you mentioned. > The {} syntax would be unambiguous in the most useful contexts (e.g. > function parameters, match arms, arrow function bodies, rhs of binary > operators, etc.). It is ambiguous in the general expression context > due to expression statements (statements containing a single > expression followed by `;`), where it's unclear (without lookahead) > whether the `{` refers to a statement block or a block expression. > Replacing all statement blocks with block expressions comes with the > added difficulty of allowing to omit the `;` of block expressions in a > expression statement. > > I remember there also being issues with the optimizer (related to > https://www.npopov.com/2022/05/22/The-opcache-optimizer.html#liveness-ran= ge-calculation). > The details went over my head at the time. > > I'm interested in picking this back up at some point, at least for match = arms. > > Ilija > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >