Re: Examples comparing Block Scoped RAII and Context Managers
| From: | Rowan Tommins [IMSoP] | Date: | Tue, 20 Jan 2026 21:41:23 +0000 |
| Subject: | Re: Examples comparing Block Scoped RAII and Context Managers | ||
| References: | 1 2 3 4 5 6 7 8 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-129798@lists.php.net to get a copy of this message | ||
On 15/01/2026 09:03, Tim Düsterhus wrote:
Existing constructs in PHP that take a list of multiple “items” behave equivalent to consecutive single-item entries. Or in less abstract terms:There's a jump here. In a grouped "const", "global", or "static" statement, the expansion is to a *series of statements* with a common prefix. In your proposed syntax, the expansion is to a *nested set of blocks*. That makes the semantics more complicated, and in my opinion more surprising... Rather than "const", a better example is probably "static", where this is legal:const FOO = 1, BAR = 2;is equivalent to:const FOO = 1; const BAR = 2;So to me it is only natural to extend that logic to thelet()construct, which only leaves the let($a) let ($b) desugaring which then implies (3).
$bar = 42; static $foo=$bar + 1, $bar = 1; echo "$foo,$bar";It desugars to a series of separate statements:
$bar = 42; static $foo=$bar + 1; static $bar = 1; echo "$foo,$bar";The static $bar silently shadows the local one, and you get an output of 43,1 Interestingly, this is *not* legal PHP:
static $foo=$bar + 1, $bar = 1, $foo = -1;"Fatal error: Duplicate declaration of static variable $foo" Note that these are two largely arbitrary design decisions: - A static variable with the same name as a local one shadows the previous variable, with no hoisting or dead zone - A static variable with the same name as an existing static variable is forbidden
This is exactly matching the proposed semantics of let().
Not quite.
It works for this:
$bar = 42;
let($foo=$bar + 1, $bar = 1) {
echo "$foo,$bar";
}
The block-scoped $bar shadows the function-local $bar, and the output is 43,1 just like with "static".
But what if we declare the same variable twice:
$bar = 42;
let($foo=$bar + 1, $bar = 1, $foo=-1) {
echo "$foo,$bar";
}
My intuition from "static", and from every other language I've used, is that this would be an Error: there's a single block, and you're trying to declare two different local variables called "$foo" inside that block.
Instead, what happens is that the second declaration of $foo shadows the first.
The justification is that this is not actually one block at all, it's secretly a whole set of nested blocks:
$bar = 42;
{
let($foo=$bar + 1) {
let($bar = 1) {
let($foo=-1) {
echo "$foo,$bar";
}
}
}
}
Yes, but the “more traditional syntax” would also leave the possibility of (1) or (2) - unless restricted to the start of the block as in C90.I suspect this is where we've been talking past each other a bit: looking at the syntax, I interpreted the comma-separated list as defining a set of variables in the same scope. That leaves all the same options as the "traditional" syntax: $bar = 42; let($foo=$bar+1, $bar=1) {
echo $foo;} - You could treat $bar as "hoisted" within the let() construct, giving $foo==2 - You could treat $bar as creating a Dead Zone within the let() construct, giving an Error - You could treat the declarations as sequential, and shadow the value, giving $foo==43 What I completely missed, and seemed obvious to you, is that there are actually two separate scopes being declared, nested inside each other. (Even so, there is another possibility: variable shadowing could be forbidden, as in C#, making the "$bar=1" declaration an error). So I guess it all comes down to the same thing: the syntax just doesn't seem intuitive to me, because I keep expecting it to behave like JS, C, Java, Perl, etc. I can't get past the fact that copying from one of those languages would be so much more obvious. -- Rowan Tommins [IMSoP]