Re: RFC: short and inner classes

From: Date: Wed, 19 Mar 2025 15:04:06 +0000
Subject: Re: RFC: short and inner classes
References: 1 2 3 4 5 6  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
On Tue, Mar 18, 2025, at 03:37, Bob Weinand wrote:
> Hey,
> 
> On 17.3.2025 19:58:39, Rob Landers wrote:
>> On Mon, Mar 17, 2025, at 19:05, Bob Weinand wrote:
>>>>> . The idea that extending the parent class doesnt no inherit the child classes
>>>>> doesnt make sense to me. 
>>>>> As then if you extend a parent class and call a function of that class which
>>>>> could rely on the existence of an inner class, I can see a lot of headaches caused by this exact
>>>>> scenario.
>>>>> As a developer, if I extend a class, I expect the entire dependance of that
>>>>> class to be inherited, otherwise the extending class won't work. 
>>>> 
>>>> I'm not sure what you mean. When you inherit a class, you do not necessarily
>>>> inherit everything from its superclass. You are free to override it however you want. Since we are
>>>> defining "types" in the sense of PHP, we cannot merely inherit a "type",
>>>> otherwise we would cause all kinds of issues with LSP. This is specifically why inheritance works
>>>> the way it does in this RFC and why static:: is forbidden.
>>> I don't understand the problem here.
>>> 
>>> for each nested class in parent class:
>>>     class_alias(substitute parent class with child class in class name, class name)
>>> Very simple. There's no magic needed, it can be simply class aliases.. This will
>>> also make static, self and parent trivially work.
>>> 
>> 
>> PHP doesn't go out of its way to prevent the developer from violating LSP -- but where
>> it can, it does. If a type were inherited, then the subclasses wouldn't be able to guarantee
>> that an inner/nested class implemented the parent inner/nested class. Or, if it did, it would
>> require that all subclasses using a class of the same name MUST inherit from the parent class as
>> well.
>> 
>> This is non-trivial to implement as the parent class may or may not have been compiled yet
>> when we are compiling the subclass. So, we have no idea what the parent class actually looks like
>> until runtime. Further, it is much simpler to reason about each class as a distinct type vs. maybe
>> inheriting a type from a supertype.
>> 
>> Thus, if you want to inherit from the super-class's inner classes, you can, but this
>> RFC won't force you to do so. In my mind, this is the biggest argument for a \,
>> because the enclosing class acts more like a namespace than a type, from the perspective of the
>> inner class.
>> 
>> If we were to embrace \, then it could be argued that a namespace is
>> technically a class in itself, (but a partial class, to borrow from C# terminology) and every class
>> in a namespace is essentially just a public class in that super-class/namespace.
>> 
>> Nobody has argued that perspective, but I believe it may be interesting (and would lay a
>> foundation for namespace private/public class behavior/visibility). That being said, it truly does
>> cause issues with autoloading -- at least, PSR-4 autoloading -- and I'm not sure whether we
>> should solve that problem here; however, it is something to be cognizant of, for sure. There are
>> other types of autoloading supported by tools, such as composer, that do not have any issues with
>> using \ as a separator.
> Okay, I see the point with LSP. I'm not sure whether we need to preserve LSP for that
> specific scenario, but neither can I say that we should ignore it.
> 
> (Effectively implementing LSP would mean that there's an implicit interface matching all
> public method signatures of the parent class, for child classes - which is doable, but possibly too
> much for the initial RFC.)
> 
> 
> I would however ask, should we not implement LSP compatible inner classes, to enforce that no
> child class may name a class the same than any non-private inner class declared by any of its
> parents, until we resolve this question (in possibly a future version of PHP).
> I do not think we should bar ourselves from allowing this in the future.
> 

I'm not sure I understand what you are asking. But I think you are saying the following should
be illegal?

class ParentOuter {
  class ParentInner {}
}

class ChildOuter extends ParentOuter {
  class ParentInner {} // not allowed
}

> 
> However nice grand unified naming schemes may seem, I don't think it's a good idea to
> pursue. Clarity and explicitness shall reign supreme here.
> I also don't think that the proposed visibilities are applicable to namespaced classes. In
> particular and in practice shared internal classes are not necessarily directly organized in a way
> it makes sense to organize inner classes. Also visibilities like protected propagate along the
> inheritance chain, something which is not given with (outer) namespaced classes.
> The less we mix these slightly different concepts, the better.
> 
> 
> "It's similar, except in these and those cases" is the death of all consistent
> experiences. Thus, let's not even attempt to pretend it is.
> 

This is true.

> 
> And not pretending starts with using a different symbol than a backslash.
> 

I have been thinking about this for a couple of days now... When thinking through the ramifications
of my decision to use :> over ::, this will also affect generics, most likely -- whenever that
happens. This is because if this RFC passes, generics will want to be consistent with whatever
exists currently.

If we were to use :>, then generics would probably look something like this to be consistent:

class Box<T> {
  public function add(self:>T $item) {}
}

The same thing would also probably apply to ::

class Box<T> {
  public function add(self::T $item) {}
}

With \, it nearly follows exactly what you would expect-ish:

use \Box\T as T;

class Box<T> {
  public function add(T $item) {}

// or without use
  public function add(Box\T $item) {}
}

With \, we can also just automatically check the current class as part of namespace
resolution when compiling:

class Box<T> {
  public function add(T $item) {}
}

This would also make it easier to user inner classes:

class Outer {
  public class Inner {}
  public function foo(Inner $bar) {}
}

The other syntax options do not allow this; at least, I don't think so. I'm very heavily
leaning towards \ as the separator.

— Rob


Thread (102 messages)

« previous php.internals (#126844) next »