Re: [RFC] Context Managers
| From: | Larry Garfield | Date: | Mon, 01 Dec 2025 20:06:27 +0000 |
| Subject: | Re: [RFC] Context Managers | ||
| References: | 1 2 3 4 5 6 7 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-129484@lists.php.net to get a copy of this message | ||
On Mon, Dec 1, 2025, at 9:38 AM, Rowan Tommins [IMSoP] wrote:
> On 01/12/2025 14:26, Tim Düsterhus wrote:
>> You are wrong here. Manually interacting with an Iterator is not just
>> safe, it is also necessary for some use cases. The simplest example
>> would be iterating two Iterators in lock-step (i.e. doing a
>>
zip()
>> operation / array_combine()).
>
>
> It is certainly *possible* to use the Iterator methods safely without a
> foreach() construct or built-in aggregate; but it's also easy to miss
> the implications, and cause very confusing behaviour. For instance, you
> could call next() in two different pieces of code, and each would miss
> half the items; or you could call rewind() on an iterator that was
> already in use elsewhere.
>
> The same is true of the ContextManager interface: you could certainly
> call enterContext() and exitContext() manually with no problems at all..
> It might even be necessary, for much the same reasons as iterators: you
> might want to write code that aggregates two context managers in a
> specific arrangement.
>
>
> As I understand it, your concern is that someone will call
> enterContext() without exitContext(). You could do exactly the same
> thing if you manually handle an Iterator.
>
> Consider this method:
>
> function getDataIterator(): Generator {
> $this->acquireLock();
> while ( $this->hasMoreResults() ) {
> yield $this->getNextResult();
> }
> $this->releaseLock();
> }
>
> If the user calls $it = $foo->getDataIterator(), they can call
> $it->current() and get a result, but never call $it->next(). They could
> even use it in a foreach() loop but break out before consuming
> all the
> results. In such cases, the lock would never be released, unless there's
> an extra safety check in the destructor.
Excellent example, thank you.
> I think the currently proposed names actually make that *less* likely
> for a ContextManager: if you enter something, you probably want to exit
> it later.
>
> We could reinforce that further by also prefixing the method names with
> "__", but we don't have any precedent for that, and misuse would still
> be possible.
Python uses magic methods here, and we considered it, but given that you will basically always want
both methods we felt an interface was easier and provided better type checks. If there were
multiple callbacks (say, a different one for a success exit and a fail exit), then magic methods
might make more sense.
If you're suggesting having interface methods that begin with __, I agree that would be novel
for no particular value. You can't screw up ContextManagers through misuse any more than you
can screw up Iterator or ArrayAccess, and we've survived just fine with those for literally
decades.
--Larry Garfield