On Thu, 7 May 2026 at 19:03 Rowan Tommins [IMSoP] <[email protected]>
wrote:
> On 07/05/2026 13:16, Bob Weinand wrote:
> > I am not opposed at all to such a consistent macro system though.
> > ... but I would not make a transaction like that a macro. For inlining
> variables in a html or sql snippet with proper escaping, definitely great.
> > To me, macros should meaningfully transform their contained code rather
> than wrapping a bit of logic around, which would be covered by this RFC.
> > It's complimentary, not a replacement.
>
>
> Thinking about this further, I agree that full-blooded macros would be
> overkill for this case, but my starting point was that closures don't
> feel like the right solution either - they're a different feature being
> bent into shape.
>
> So rather than starting with a Closure, and taking things away, I was
> trying to think of ways to start with a simple block of code, and build
> it up.
>
>
> For instance, the array_walk example in the RFC is just an ugly (and
> probably inefficient) way of writing a foreach loop:
>
> array_walk($numbers, fn($number) {
> $count = bcadd($count, '1');
> $sum = bcadd($sum, $number);
> });
>
> foreach($numbers as $number) {
> $count = bcadd($count, '1');
> $sum = bcadd($sum, $number);
> }
>
> So, what would it look like to generalise that, so we could do the same
> for other scenarios, like async() and transaction()?
>
>
> A simple first step would be something that could take a block of code,
> and say when to execute it:
>
> custom_block transaction($dbConnection) {
> try {
> $dbConnection->beginTransaction();
> __execute_block_body();
> }
> catch (\Throwable $e) {
> $dbConnection->rollbackTransaction();
> throw $e;
> }
> $dbConnection->commitTransaction();
> }
>
> transaction($myDb) {
> $myDb->query('UPDATE ...');
> }
>
> If the block body is never represented as a variable, there's no need to
> define what happens when it outlives scope, or the user tries to clone
> it, rebind it, etc.
>
> And if the calling code doesn't look like it's creating a Closure, users
> won't have any wrong expectations about variable scopes or lifetimes.
>
>
> Onto that, we can add syntax to push values into the block, e.g. using
> the "as" keyword like in "foreach" (example based on the "Generator
> decorator managers" section at the end of the Context Managers RFC):
>
> custom_block opening($filename) {
> $f = fopen($filename, "r");
> if (!$f) {
> throw new Exception("fopen($filename) failed");
> }
> try {
> __execute_block_body($f);
> } finally {
> fclose($f);
> }
> }
>
> opening(__FILE__ as $f) {
> var_dump($f);
> }
>
>
> And some way to pull values out, e.g. a "break with" keyword:
>
> custom_block transaction(DBConnection $conn) {
> $conn->beginTran();
> try {
> if (__execute_block_body() === DBConnection::TRANSACTION_ABORT) {
> $conn->rollbackTran();
> return;
> }
> } catch (\Throwable $e) {
> $conn->rollback();
> throw $e;
> }
> $conn->commitTran();
> }
>
> transaction($connection) {
> $affectedRows = $connection->query("UPDATE ...");
> if ($affectedRows === 0) {
> break with DBConnection::TRANSACTION_ABORT;
> }
> // ...
> }
That looks like a function (aka closure) with a lot of weird extra steps.
It’s been years, several proposals and multiple demonstrations on how arrow
function syntax can be expanded to be useful with multi-statements. So much
gymnastics goes on to deny such a simple and elegant syntax.
>