On 16/11/2025 15:29, Frederik Bosch wrote:
On Sat, 15 Nov 2025 23:11:44 +0000, Rowan Tommins [IMSoP] wrote:
The Block Scoping RFC and the Context Manager RFC cover a lot of similar
use cases, and a lot of the discussion on both threads has been
explicitly comparing them.
To try to picture better how they compare, I have put together a set of
examples that implement the same code using both features, as well as
some other variations, in this git repo:
https://gitlab.com/imsop/raii-vs-cm
Another suggestion would be to follow the Java try-with-resources
syntax. It does not require a new keyword to be introduced, as with
the Context Manager syntax. Moreover, it aligns with current
try-catch-finally usage already implemented by PHP developers.
try ($transaction = $db->newTransaction()) {
$db->execute('UPDATE tbl SET cell = :cell', ['cell'=>'value']);
}
I did have a look into that when somebody mentioned it earlier, and I
believe it is almost exactly equivalent to C#'s "using" statement:
- C#: keyword "using", interface "IDisposable", method "Dispose":
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using
- Java: keyword "try", interface "AutoCloseable", method "close":
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
The main difference I've spotted is how it combines with other blocks.
In Java, you can use the same block as both try-with-resources and
try-catch:
try ( Something foo = new Something ) {
blah(foo);
}
catch ( SomeException e ) {
whatever();
}
In C#, you can instead use a statement version of using within any
existing block:
try {
using ( Something foo = new Something );
blah(foo);
}
catch ( SomeException e ) {
whatever();
}
Both are very similar to RAII, but because both languages use
non-immediate garbage collection, the method is separate from the normal
destructor / finalizer, and other references to the "closed"/"disposed"
object may exist.
At the moment, I haven't included examples inspired by these, because I
thought they would be too similar to the existing RAII examples and
clutter the repo. But if there's a difference someone thinks is worth
highlighting, I can add one in.
Any object that implements TryWithContext can be used with such
syntax. The function returns the exit context operation as callback.
interface TryWithContext {
public function tryWith(): \Closure;
}
I can't find any reference to this in relation to Java; did you take it
from a different language, or is it your own invention?
Either way, it looks like an interesting variation on the Python-based
enterContext/exitContext. Do you have any thoughts on what it's
advantages or disadvantages would be?
Rather auto-capture I'd suggest explicit complete scope capture, by
using the use keyword without parenthesis.
This is a completely separate discussion I was hoping not to get into.
Although I've personally advocated for "function() use (*) {}" in the
past, I've used "fn() {}" in the "auto-capture" examples because it is
the syntax most often proposed.
It's irrelevant for this example anyway, so I've edited it out below.
For a transaction it might look like this.
class Transaction implements TryWithContext {
public function tryWith(): \Closure
{
$this->db->beginTransaction();
return function (?\Throwable $e = null) {
if ($e) {
$this->db->rollbackTransaction();
return;
}
$this->db->commitTransaction();
};
}
}
Looking at this example, it feels like it loses the simplicity of RAII
without gaining the power of Context Managers. In particular, tryWith()
as shown can't return a separate value to be used in the loop, like
beginContext() can in the Python-inspired proposal.
The class would have a separate constructor and tryWith() method, with
no clear distinction. Making the cleanup function anonymous prevents the
user directly calling dispose()/close()/__destruct() out of sequence;
but it doesn't stop the object being used after cleanup, which seems
like a more likely source of errors.
Still, it's interesting to explore these variations to see what we can
learn, so thanks for the suggestion.
--
Rowan Tommins
[IMSoP]