Re: RFC: short and inner classes

From: Date: Thu, 20 Mar 2025 16:16:12 +0000
Subject: Re: RFC: short and inner classes
References: 1 2 3 4 5 6 7 8 9 10 11 12  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message


On Thu, Mar 20, 2025, at 16:41, Bob Weinand wrote:
> 
>> Am 20.03.2025 um 16:28 schrieb Rob Landers <[email protected]>:
>> 
>> 
>> 
>> On Thu, Mar 20, 2025, at 16:12, Bob Weinand wrote:
>>> 
>>>> Am 20.03.2025 um 15:51 schrieb Rob Landers <[email protected]>:
>>>> 
>>>> 
>>>> 
>>>> On Wed, Mar 19, 2025, at 21:09, Bob Weinand wrote:
>>>>> 
>>>>> 
>>>>> On 19.3.2025 16:04:06, Rob Landers wrote:
>>>>>> On Tue, Mar 18, 2025, at 03:37, Bob Weinand wrote:
>>>>>>> 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
>>>>>> }
>>>>> Precisely.
>>>>> 
>>>>>>> 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
>>>>> I'm failing to understand why you'd think this would be related at
>>>>> all?
>>>>> 
>>>>> If we get generics,
>>>>> 
>>>>> class Box<T> {
>>>>>   public function add(T $item) {}
>>>>> }
>>>>> 
>>>>> would just work, without any use or such. There will not be a symbol Box::T or
>>>>> Box\T, just all mentions of T within the Box class will be taken as "this is the generic
>>>>> placeholder" and the compiler takes care.
>>>>> It's not like that T will be directly accessible from outside of the class
>>>>> or actually a proper type, unlike inner classes.
>>>>> 
>>>>> A generic is not an inner class nor will it look like it. Also, there's no
>>>>> accessing of a parents generic - you write class Child<T> extends ParentClass<T> - or
>>>>> something along these lines, getting the T available for your class.
>>>>> 
>>>>> Bob
>>>> 
>>>> Yes, that is the question right? It might not affect anything there, but there
>>>> would probably be an argument to keep it consistent with inner classes. In PHP, a class is a type;
>>>> thus an inner class is an inner type, and generic types are also an inner type that only exist in
>>>> the scope of their enclosing class, just like private inner classes.
>>>> 
>>>> If my logic is incorrect, let me know.
>>>> 
>>>> — Rob
>>> 
>>> The difference is that inner classes are a backed type; there's actually a
>>> class/interface/enum/... behind it.
>>> 
>>> Generic placeholders are placeholders for another type, which is strictly local to the
>>> compilation of the class.
>>> 
>>> Bob
>> 
>> If that were the case, then this would be an error:
>> 
>> function add(T $item) {
>>   if ($item instanceof T) {}
>> }
>> 
>> because T isn't a backed type. I don't think they are the same thing, but during
>> runtime, T is most definitely a backed-type; not an empty box. You still have to refer to T in some
>> way, and I'm of the opinion that self:>T is not ideal (I think we can probably all agree on
>> that?). Even in inner classes, it seems to make more sense to just treat it like you would any other
>> type in the current namespace.
> 
> It's a *placeholder* for a backed type.
> At runtime (or during monomorphization), the T will be replaced by the actual type.
> 
> Whatever name T refers to is the actual backed type.
> 
> Bob

Right, my point is that you have to refer to T in the first place. If we use \, then T
as-is is fine, because there is a clear resolution to what T you are referring to. If we use :>
or ::, then when you write Box<T>, are you referring to T, as in the generic T; or T as in the
class T in the same namespace. This is clear when we use "T" literally, but quickly
becomes not so clear when using something else:

class Item {}

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

This is exactly why I said this matters! In the case above with the current :> syntax, we should
probably use:

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

to explicitly define that we want the inner type "Item." To use the first example, you
have to define inner types to allow that, but we are currently defining inner types in this RFC.
I'd personally rather use the first example when it comes to generics, which means we need to
define that now. In other words, I think you are on team "\" without realizing you are on
team "\".

— Rob


Thread (102 messages)

« previous php.internals (#126868) next »