Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129288 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 94D491A00BC for ; Tue, 18 Nov 2025 17:25:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1763486723; bh=VOrLdTN3KREiw0w7+a2gOramQxnv0f2EieT1nFKvaYo=; h=Date:From:To:In-Reply-To:References:Subject:From; b=lrZ/yLEArqbWBvfTREWpAioCc2t6ORSW4AGaUtxHL6lhHQlFkhEqiyPceQkouQ/LS hDPr6H3wuR7lAyjgsd8SfhsXZBzZtIplhAJZGqyw6GHqWpk/gHAZu6TbHDotKCMOMf dPgzDLCsXex0tV7Wm1uF226EsDggMHEWDUamaqAQ63ofvMU0Uph6YDEaC+0GVLndRe STHclQFXQL5jUtfPfVegYj+FGX8VuxHvJf4LqJx7pWvMhADdtYFaYVm0MZFbkw6QK8 zplRuhvbynHeG0AEji12lBkZIIsh5p7szXtTtYDxctzNH/ESuQbX8OAjFpZhJKC+Hq bvJAOkZP1Y8tA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2FF191801D5 for ; Tue, 18 Nov 2025 17:25:22 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_40,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fout-b3-smtp.messagingengine.com (fout-b3-smtp.messagingengine.com [202.12.124.146]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 18 Nov 2025 17:25:21 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailfout.stl.internal (Postfix) with ESMTP id 4B9BA1D0032C for ; Tue, 18 Nov 2025 12:25:16 -0500 (EST) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-04.internal (MEProxy); Tue, 18 Nov 2025 12:25:16 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to; s=fm3; t=1763486716; x=1763573116; bh=UOit2KO8lywi/fwNVNqlV PPhhLYoyEoTam3MyMwddw8=; b=EGn9ZaQ5P8ZLy8XCPbECPLQkQonWcKwur/THD dls9oy4qlzUK5cyc7Xr8B/cnbto0KIRdwvJjLzNCj6CKMsjTBTfBuV/p07UFdiYE chDGy9Unv4S688krZ5Zp99upFSPTH3PiG9MOw4eaGLK4msoDSGvpTGLYOpat0iFc H4oCeFpGWXfP2CDWFOWyC1eBB9wMqUK6uQ8WoZ1W1m5yNscUXBuyNaxifcYNI2g2 Exg7GwqEdgCKbKJE1QUaa+ldrZB7IeRALC8WbfgN4hl+TDG3RnpXkEY9uUK0fKy0 TOFPCyVH2B5Eqo/d49uxF0icwq7iezI7trL974U7kYYZ5wx9w== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1763486716; x=1763573116; bh=U Oit2KO8lywi/fwNVNqlVPPhhLYoyEoTam3MyMwddw8=; b=ZA1JTBF2Kf7tJ9szp jM/RpIAVu/5kSsoMn8CltS2AevZgO2MZCh4uflbQTHR/Iu0yOekLcBhiixGpPqUL qaHnPyF4jtWdck67tBk9ndFtXU9LLqX3aQ9GvfhoCTeDyOj0EA/UTnP2gE6dlbqh sDv33vINkQkvX83/qi9aLFcA4Wh+4t/z5pojt63Fqyl2lgJs5BKeCgGImtR87hsq lU5Zyzv4hpvDhSxQwFmkuTU2p2on2EQ4KPruBCk5FJtNmkBs/VyYrtes58nXtWtN HibTNFcobz8WRykBSjOA7edPHxsiSSEsb1BzZx3kCKjV5VdwMTyMlUDObZ4ZZDjn +TZ0w== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddvvdduledvucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvffkjghfufgtgfesthhqredtredtjeenucfhrhhomhepfdfnrghrrhih ucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmqe enucggtffrrghtthgvrhhnpeelheevteefteduffegveelffefleevvdejvedvfeeijeef udfgteetieehjedtvdenucffohhmrghinhepghhithhlrggsrdgtohhmnecuvehluhhsth gvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheplhgrrhhrhiesghgrrhhf ihgvlhguthgvtghhrdgtohhmpdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpoh huthdprhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id CD8E2700054; Tue, 18 Nov 2025 12:25:15 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: AQWJq8Gl4GWJ Date: Tue, 18 Nov 2025 11:23:35 -0600 To: "php internals" Message-ID: <432ca4ad-7bcc-43bd-8e05-3121839b4ff7@app.fastmail.com> In-Reply-To: <26a2f13c-f318-4d6c-9595-bfaaebcbabcb@rwec.co.uk> References: <26a2f13c-f318-4d6c-9595-bfaaebcbabcb@rwec.co.uk> Subject: Re: [PHP-DEV] Examples comparing Block Scoped RAII and Context Managers Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Sat, Nov 15, 2025, at 5:11 PM, Rowan Tommins [IMSoP] wrote: > Hi all, > > The Block Scoping RFC=C2=A0and the Context Manager RFC cover a lot of = similar=20 > use cases, and a lot of the discussion on both threads has been=20 > explicitly comparing them. > > To try to picture better how they compare, I have put together a set o= f=20 > examples that implement the same code using both features, as well as=20 > some other variations, in this git repo: https://gitlab.com/imsop/raii= -vs-cm > > A few notes: > > - The syntax for the two proposals is based on the current RFC text. I= f=20 > they are updated, e.g. to use different keywords, I will update the=20 > examples. > > - I have included examples with=C2=A0closures which automatically capt= ure by=20 > value, since a lot of the same use cases come up when discussing those. > > - There are many scenarios which could be included, and many ways each=20 > example could be written. I have chosen scenarios to=C2=A0illustrate c= ertain=20 > strengths and weaknesses, but tried to fairly represent a "good" use o= f=20 > each feature. However, I welcome feedback about unintentional bias in = my=20 > choices. > > - Corrections and additional examples are welcome as Merge Requests to=20 > the repo, or replies here. > > > With that out of the way, here are my own initial thoughts from workin= g=20 > through the examples: > > - RAII + block scope is most convenient when protecting=C2=A0an existi= ng=20 > object which can be edited or extended. > > - When protecting a final object, or a native resource, RAII is harder=20 > to implement. In these cases, the separation of Context Manager from=20 > managed value is powerful. > > - Context Managers are very concise for safely setting and resetting=20 > global state. RAII can achieve this, but feels less natural. > > - An=C2=A0"inversion of control" approach (passing in a callback with = the=20 > body of the protected block) requires capturing all variables *not*=20 > scoped to the block. Even with automatic by-value capture, those neede= d=20 > *after* the block would need to be listed for capture by reference. > > - Building a Context Manager from a Generator can lead to very readabl= e=20 > code in some cases, and closely mimics an "inversion of control"=20 > approach without the same variable capture problems. > > > I would be interested in other people's thoughts. > > Regards, > > --=20 > Rowan Tommins > [IMSoP] Thank you to Rowan for the in depth comparison! One thing I definitely do not like is the need for a `FileWrapper` class= in the RAII file-handle example. That seems like an unnecessary level = of abstraction just to squeeze the `fclose()` value onto the file handle= . The fully-separated Context Manager seems a more flexible approach. Suppose we were dealing with a considerably more involved object than a = file handle, with a large interface. You'd need to either 1. Extend the class, and we all know about extends... 2. Make a wrapper that passes through the calls (which could be very ver= bose) 3. Do as is done here, with a simple dumb wrapper, which means in the bo= dy of the context block you have to do `$var->val` all the time, which i= s just clunky. Fully separating the Context Manager from the Context Variable completel= y avoids that issue. I also noted that all of the examples wrap the context block (of whichev= er syntax) in a try-catch of its own. I don't know if that's going to b= e a common pattern or not. If so, might it suggest that the `using` blo= ck have its own built-in optional `catch` and `finally` for one-off addi= tional handling? That could point toward the Java approach of merging t= his functionality into `try`, but I am concerned about the implications = of making both `catch` and `finally` effectively optional on `try` block= s. I am open to discussion on this front. (Anyone know what the typica= l use cases are in Python?) Regarding `let`, I think there's promise in such a keyword to opt-in to = "unset this at the end of this lexical block." However, it's also off t= opic from everything else here, as I think it's very obvious now that th= e need to do more than just `unset()` is common. Sneaking hidden "but i= f it also implements this magic interface then it gets a bonus almost-de= structor" into it is non-obvious magic that I'd oppose. I'd be open to = a `let` RFC on its own later (which would likely also make sense in `for= each` and various other places), but it's not a solution to the "package= d setup/teardown" problem. --- Another thing that occurs to me, from both this writeup and the discussi= on in both threads, is that "escaped variables" are an unsolvable proble= m. If a context variable is "open" (for some generic definition of open= ; that could be a file handle or an unflushed buffer or DB transaction o= r just a large memory sink), and then a reference to it is saved elsewhe= re, then when the context block ends, there's two problems that could ha= ppen: * If the context block force-closes the variable, then the escaped refer= ence is no longer valid. This may or may not cause problems. * If the context block does not force-close the variable, then we can't = know that the end of the context block has flushed/completed the process= . This may or may not cause problems. Which one is less of a problem is going to vary with the particular situ= ation. I don't think we can make a language-wide statement about which = is always less problematic. That means we need to allow individual case= s to decide for themselves which "leak problem" they want to have. Which is exactly the benefit of the separation of the Context Manager fr= om the Context Variable. The CM can be written to rely on `unset()` clo= sing the object (risk 2), or to handle closing it itself (risk 1), as th= e developer determines. Moreover, it's possible to have two different strategies for the same co= ntext variable, as either 2 separate Context Managers or one manager wit= h a constructor parameter. Suppose the context variable is a `Buffer` instance of some kind, which = has a `flush()` method and a destructor that calls `flush()`. Both `For= cedBufferContext` and `LazyBufferContext` could return the same Buffer c= lass, but have different approaches to when the flush happens. That's a= level of flexibility that's impossible to achieve if the "exit" logic i= s on the context variable itself, whether in the destructor or a separat= e interface method. Alternatively, `new BufferContext(force: true)` (or whatever) would avoi= d the need for 2 classes, depending on the specifics of the use case. To, me, that's a strong argument in favor of the Context Manager approac= h.