On Tue, Mar 25, 2025, at 19:51, Larry Garfield wrote:
> On Tue, Mar 25, 2025, at 6:59 AM, Rob Landers wrote:
> 
> >> When I say module scope, I'm referring to something along the lines that 
> >> Arnaud and I were exploring a while back.  tldr, "cluster of files with a 
> >> common namespace root, which can get loaded together."  It was mostly 
> >> about performance, but did offer module-private as well, with some nice 
> >> potential.  At the moment it's stalled out on "there's nasty hard edge 
> >> cases and we're not sure if it's worth it" concerns.
> >> 
> >> Concept brain dump here: 
> >> https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md
> >> Code exploration from Arnaud here: 
> >> https://github.com/arnaud-lb/php-src/pull/10
> >> 
> >> Still well short of RFC state, of course, but provided for context.
> >
> > My email has been broken for a few days, so sorry for the late response...
> 
> No worries, I'm about to leave town myself. :-)
> 
> >> In this case, I am not seeing what the nesting gets you.  Making 
> >> Destination a normal class doesn't hurt anything here, does it?
> >
> > I wasn't intending to write a definitive example, just to illustrate 
> > it. A better example might be a lazy cache, using hooks to notify the 
> > outer class it has possibly been updated: 
> >
> > (note this is using the new syntax, but this syntax is NOT currently 
> > reflected in the RFC text, yet)
> >
> > namespace Caching;
> >
> > class Cache {
> >     private array $items = [];
> >     private array $dirty = [];
> >
> >     public function getItem(string $key): CacheItem {
> >         return $this->items[$key] ?? ($this->items[$key] = new 
> > CacheItem($this, $key, null));
> >     }
> >
> >     public function saveChanges(): void {
> >         foreach ($this->dirty as $key => $value) {
> >             echo "Saving $key to persistent storage\n";
> >         }
> >         $this->dirty = [];
> >     }
> >
> >     private function markDirty(string $key): void {
> >         $this->dirty[$key] = $this->items[$key];
> >     }
> >
> >     public class CacheItem {
> >         public string|null $value {
> >             get {
> >                 return $this->_value;
> >             }
> >             set {
> >                $this->_value = $value;
> >                $this->cache->markDirty($this->key);
> >             }
> >         }
> >
> >         public function __construct(
> >             private readonly Cache $cache,
> >             public private(set) string $key,
> >             private string|null $_value,
> >         ) {}
> >     }
> > }
> >
> > $cache = new Cache();
> > $item1 = $cache->getItem('foo');
> > $item1->value = 'bar';
> >
> > $cache->saveChanges();
> >
> > This outputs:
> >
> > Saving foo to persistent storage
> >
> > This provides for a simple API for outside the Cache class: getItem() 
> > and saveChanges(); that's it. For CacheItem's, you only have the props 
> > for the key or value, and updating the value marks the item as "dirty".
> >
> > Currently, if you want this same API, the only way to do this is by:
> >
> > 1. using reflection,
> > 2. using a closure and binding it to the other class's scope,
> > 3. providing a listener + observer (more formal version of [2]), or
> > 3. iterating over items while saving to find dirty items.
> >
> > Those choices are not ideal (IMHO).
> >
> > Reflection feels "hacky" as does using a bound closure. The 
> > listener/observer pattern is probably overkill here, unless you already 
> > have one lying around (i.e., using symfony/laravel or a related 
> > framework), but still might require making some of the "internal" api 
> > public. Finally, iterating over all the items to find dirty ones is 
> > naive and inefficient.
> >
> > Hopefully this is a better illustration of what nesting provides?
> >
> > -- Rob
>  
> I have a similar if less involved use case in my ordering library, though it 
> doesn't need the look-back functionality.
> 
> So the use case that nested classes would enable that isn't currently covered 
> by "just use separate files and @internal, deal" is around the lesser class 
> having private access to the greater class.  Which... I believe fileprivate 
> and modules would also address, albeit in a different way, yes?  (Presuming 
> fileprivate is available on properties, not just the class itself.)
> 
> --Larry Garfield
Well ... to use the best engineering response: "it depends..."

File-private, in my mind, is different; such as when the classes are distinctly 
unrelated with respect to each other, but nobody else should be able to 
instantiate or use the private class. It is like it doesn't exist outside that 
file. For example, something like a log formatter or a default strategy 
implementation. Personally, I'd feel that file-private should be kept as simple 
as possible and limit it to "top-level" things, but that doesn't necessarily 
have to be the case. If we did allow it on methods/properties, when mixing it 
with regular visibility, what happens? `fileprivate public private(set)` ... 
means what exactly? I assume we probably wouldn't allow that particular 
scenario, and maybe `fileprivate` on a property means `public` in the file, but 
`private` outside the file. But then how would that intersect with inheritance? 
My point is that I don't think there is an intuitive answer to these behaviors, 
but at least nested/inner classes, while not 100% intuitive either, at least 
are available in other languages, so its behavior will be familiar.

If we were to target php 9, then we could simply redefine what private even 
means, similar to Rowan's findings up thread (emphasis mine), and not even have 
a special keyword:

On Mon, Mar 24, 2025, at 20:22, Rowan Tommins [IMSoP] wrote:
> If you'll excuse a brief philosophical detour, I noticed something 
> interesting in the Swift documentation: the description of nested classes 
> doesn't describe any special scope access. Instead, the description of 
> "access levels" defines "private" in a way that naturally includes them:
> 
>> Private access restricts the use of an entity to the enclosing declaration, 
>> and to extensions of that declaration that *are in the same file*.
> 
> It's a subtly different framing - nested types aren't breaking into the 
> private entity, the "private" keyword is explicitly letting nested types in.

Module-private, on the other hand, would be a huge boon to php; for a number of 
reasons. After reading your brain-dump, it would be really interesting if we 
could package a binary representation of the module alongside the module itself 
-- maybe not in an initial version, but later -- so that it could basically be 
"pre-loaded" right into OPcache... But I digress. Your module idea looks far 
simpler to implement than this one :D 

I'd be happy to give it a go, if you're up for it and no one else is working on 
it. It may be a few months, though; if this one passes, I want to go for 
short-classes next, plus I assume there is an expectation that I would fix bugs.

— Rob

Reply via email to