On 19/01/2026 15:58, Larry Garfield wrote:
In an ideal world, if we had auto-capturing long-closures, then I would agree 
this is largely unnecessary and could instead be implemented like so (to reuse 
the examples from the RFC):

$conn->inTransaction(function () {
   // SQL stuff.
});

...

If we had auto-capturing closures, I would probably argue that is a better 
approach.


I haven't caught up with the discussion fully, but I want to pick up on this specifically, because I disagree.

Inversion of control like this would only be suitable in the general case if we had auto-capture *by reference*. I believe every proposal so far has limited automatic capture to *values only*.

Auto-capture by value helps you get values *into* the closure, but does not help get anything back *out*.

So, if the code you want to sugar looks like this:

``` try{
$db->beginTransaction();
// ...
$newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId');
$newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId');
// ...
$db->commitTransaction();
}
finally{
if( $db->isInTransaction()) {
$db->rollbackTransaction();
} } // use $newFooId and $newBarId here
```


Then your options with a capture-by-value closure are either

a) list the outputs as manual by-ref captures:

``` $newFooId = $newBarId = null;
$db->inTransaction(fn() use (&$newFooId, &$newBarId) {
// ...
$newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId');
$newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId');
// ...
}); // use $newFooId and $newBarId here
```


or b) return the outputs, and extract them using array destructuring or similar:

``` [$newFooId, $newBarId] = $db->inTransaction(fn() {
// ...
$newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId');
$newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId');
// ...
    return [$newFooId, $newBarId];
});// use $newFooId and $newBarId here
```


A Context Manager - or any other syntax based on a code block rather than a full stack frame - instead gives you direct access to the local variables:

``` using($db->transaction()) {
// ...
$newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId');
$newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId');
// ...
}// use $newFooId and $newBarId here
```


Even if we had automatic capture by value, I think Context Managers would be a useful proposal to discuss.


--
Rowan Tommins
[IMSoP]

Reply via email to