Am 20.03.2025 um 16:28 schrieb Rob Landers <rob@bottled.codes>:
On Thu, Mar 20, 2025, at 16:12, Bob Weinand wrote: Am 20.03.2025 um 15:51 schrieb Rob Landers <rob@bottled.codes>: 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