Re: [RFC] Context Managers
| From: | Matthew Weier O'Phinney | Date: | Tue, 16 Dec 2025 21:40:49 +0000 |
| Subject: | Re: [RFC] Context Managers | ||
| References: | 1 2 3 4 5 6 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-129627@lists.php.net to get a copy of this message | ||
On Tue, Dec 16, 2025 at 10:01 AM Larry Garfield <larry@garfieldtech..com>
wrote:
> On Tue, Dec 16, 2025, at 5:25 AM, Deleu wrote:
> > On Tue, Dec 16, 2025 at 5:10 AM Rowan Tommins [IMSoP]
> > <imsop.php@rwec.co.uk> wrote:
> >> (Replying to a few of you in one go, I hope the formatting is clear)
> >>
> >> >>> we have changed the RFC to use => instead. So the new syntax is
> >> >>>
> >> >>> using (new CM() => $cVar) {
> >> >>> // Do stuff here.
> >> >>> }
> >> >>>
> >> >>
> >> >> Going to be controversial here, but this is confusing, because it
> operates
> >> >> in the exact opposite of every other usage of => we have.
> >> >
> >> > I agree with Matthew.
> >> >
> >> > I think it makes more sense to reverse them, like this:
> >> >
> >> > using ($cVar => new CM()) {
> >> > // Do stuff here.
> >> > }
> >>
> >>
> >> It took me a long time to figure out what you were both saying here,
> because to me the direction of the arrow seems to consistently indicate
> data flow: you call the function, and data comes out; you enter the
> context, and a variable comes out.
> >>
> >> But I think I see it now: you're treating the context variable like an
> array key, that is somehow "bound to" the result. Except that's not what's
> happening, otherwise we could just use "=".
> >>
> >>
> >> >I also agree with Matthew but the reversed proposed here looks very
> very
> >> >awkward to me. I think the most natural thing is the “as” but I may
> have
> >> >missed the discussion on why it had to be changed.
> >> >
> >> >Thinking of foreach ($array as $value), an item from the array (left)
> is
> >> >assigned to $value (right).
> >>
> >>
> >> This is the way to read the "as" syntax, yes; as I've said in a few
> places, a Context Manager is like an iterator that goes around once.
> >>
> >>
> >> > That seems symmetrical to using (new Manager as
> >> >$manager) where the instance (left) is assigned to the variable
> (right).
> >>
> >>
> >> This, however, is why it was changed: that is *not* what is happening.
> The Context Manager is not assigned to the variable, it *produces a value*
> which is assigned to the variable.
> >>
> >> Again, look at the foreach equivalence: you wouldn't write "foreach(new
> MyIterator as $iterator)", because it's not the iterator that ends up in
> the variable, it's the *items produced by* the iterator.
> >>
> >> So you have "foreach(new MyIterator as $item)" and "using(new
> MyContextManager as $contextVar)".
> >>
> >> But evidently that is not obvious to readers.
> >
> > My conclusion here is exactly the opposite of yours because this
> > explanation makes
as even more symmetrical now. As
> > you've mentioned
> > foreach(new MyIterator as $iterator) is not how it works.
> > An iterator
> > *produces a value* that is assigned to the variable after as
> >.
> > Symmetrically, using(new ContextManager as $context) the
> > ContextManager is also producing a value that is assigned to the
> > variable after as. The only difference is in the keyword:
> > foreach
> > will loop, but using will not loop since ContextManager is not an
> > iterator.
> >
> > I'm assuming this would also be a valid syntax: `using ($manager = new
> > ContextManager as $context)` which gives us a shot at capturing the
> > context manager.
> >
> > I wrote a little snippet to try and see (using GitHub Gist Syntax
> > Highlighter) how some of the options would look like once IDE support /
> > syntax highlighting is baked in:
> >
> > image.png
> >
> > Ultimately, people will take a quick reading in a Context Manager
> > documentation and the parallels to explain how it works seems to be
> > very well in place for as, imo.
> >
> > In the image above I tried adding for using because I was
> > trying
> > really hard to come up with a word that could replace each
> > in
> > foreach to make it even more symmetrical, but I could not
> > find
> > anything decent.
> >
> > - for context(new ContextManager as $context)
> > This one reads well, but the fact its two words (and a long word for
> > that matter) doesn't sit well.
> >
> > - for with(new ContextManager as $context)
> > I'm not a native English speaker, but I think this doesn't read at all
> >
> > - for one(new ContextManager as $context)
> > Kind of implies it works with iterators (similar to each), which is not
> > exactly the case and the language has very little motivation to create
> > an iterator that loops only once just to make this work.
> >
> > - for using(new ContextManager as $context)
> > Similar thoughts as for context, the only difference that
> > makes me
> > like this one more is because for context doesn't sit
> > well with `try
> > context while for using could go well with
> > try using`.
> >
> > In any case, the longer I sit with this the more I dislike
> > => for
> this.
> >
> > In an array, => is an assignment of key/value pairs.
> > For foreach ($iterator as $key => $value), the => is a destructing
> > instruction that aligns with how the array was originally assigned.
> > For the arrow function, it's kind of implied in the name ARROW function
> > that we would need a syntax in the lines of fn () =>. I
> > don't think
> > this relates well with arrays, but it works well to me because of how
> > universal arrow functions are in Javascript.
> > For match ($value), it reads as `WHEN ($value) MATCH '' THEN. The arrow
> > here => is the THEN.
> >
> > My conclusion is that => is either a value placement
> > (for arrays) or
> > an instruction execution (for arrow function and match). As such, If =>
> > were to be incorporated into Context Managers, I think it would make
> > much more sense like this:
> >
> > using (new ContextManager as $context) => [single line
> > expression]
> > where the arrow creates a symmetry with how arrow functions work.
>
>
> First, yeesh, where were y'all 2 weeks ago? :-P
>
> Second, I find it fascinating that there's so many different
> mutually-incompatible spins on what => means. As argued in the
> short-functions and auto-capture-closure RFCs from a few years ago, => in
> nearly all cases currently means "evaluates to."
>
> $arr = [
> 'a' => 'A",
> 'b' => 'B',
> ];
>
> $arr['a'] // This "evaluates to" A
>
> match ($foo) {
> 'a' => 'A',
> 'b' => 'B', // The 'b' case "evaluates to" B
> };
>
> fn($foo) => 'bar'; // This function "evaluates to" bar
>
> foreach ($arr as $k => $v) {}
>
> The last is a little bit inconsistent, but can still be read as "$k, and
> the thing that $k evaluates to."
>
This is not at all how I read it. "evaluates to" is an assignment, and
assignments in PHP flow right to left.
In the case of an array, if I access the "a" key above, I get the value
"A". For your match example, if the match applies to "a", I get the value
"A". For an arrow function, I get the callable with the return. In other
words, the expression on the right is either evaluated or assigned to the
thing on the left.
With the proposed syntax, "using ($expression => $variable) { ... }", this
reads backwards: the expression is now on the left, and is captured to the
thing on the right. That's what I'm saying will be confusing.
>
> So given that, and as Rowan noted the context manager is *NOT* the context
> variable:
>
> using ($cm evaluates to $cv) {}
>
> That's the meaning we're going for.
>
And again, that doesn't make sense from typical assignment rules. This is
why the other proposal's syntax ("let ($var = expression)") is not having
the same debate - the syntax is consistent with existing usage. It's also
consistent with how other scripting languages handle block scoping of
variables (see JS, shell scripting).
>
> If there's some better word than =>, we're still open to it, but I am
> going to insist on left-to-right reading. $cm is created first, then it
> produces $cv.
>
> 'as' is what we had originally, since that's what Python used. However,
> it was pointed out that it was confusing as it implied the CM and CV were
> the same thing, or rather the expression on the left gets assigned to $cv,
> which is not the case. Hence the change.
>
I didn't find it ambiguous, but I can see where others might. That said, I
found it _less_ ambiguous than => here.
>
> A symbol does have the (dis?)advantage of a somewhat squishier meaning
> than a keyword.
>
> I want block scoping; I have been bitten by accidental
re-assignment within a block many times, and hate having to come up with an
ever-so-slightly-different variable name to disambiguate. I don't care if
it's this proposal or the "let" proposal in terms of how the engine handles
it - but the syntax of "using (expression => $var)" isn't going to get my
vote due to how easily it can be written incorrectly.
I have no problem with the "using (expression as $var)" syntax, and the
"let($var = expression) syntax is just fine for me as well.
--
Matthew Weier O'Phinney
mweierophinney@gmail.com
https://mwop.net/
he/him