On Mon, May 11, 2026, at 10:33 AM, Rob Landers wrote:
> On Mon, May 11, 2026, at 16:58, Daniel Scherzer wrote:
>> Surprisingly I think adding an entirely new syntax would actually result in
>> the fewest breaking changes when userland classes add friends, because there
>> is no ambiguity. `$u->id` always refers to the `id` on whatever class `$u`
>> happens to be, including applying any shadowing; if you want to be sure to
>> access the base `User::$id`, use `<$u as User>->id`.
>>
>> Before I dive in and actually add that, what do people think?
>
> By the end of it, I basically arrived at a calculus that makes a sorta
> sense. Visibility levels are sets of callers, partial-ordered by
> inclusion. An override is admissible iff the child's caller set is a
> superset of the parent's at the call site. P's caller set for `$x` with
> `friend F` is {P, F}; C's shadowing with `protected $x` gives {C ∪
> descendants(C)}; incomparable, so the override violates LSP.
>
> Through that lens, we can look at your options you identified.
>
> Option 1 basically kills the entire feature.
>
> Option 3 breaks polymorphism. It throws away legitimate overrides.
>
> Option 4 is an escape hatch, not a solution. There's already RFCs for
> "as" in-progress, so you'd step on some toes there. Heh, I think I have
> even proposed "as" before and ... from experience, competing with
> someone's in-progress RFC without discussion with them beforehand is a
> great way to have people get mad at you on this list.
>
> Option 2 is probably the closest "right" answer. It prevents there from
> being invalid caller sets and can provide meaningful error messages at
> compile time: "name collides with friended private variable; must
> friend with class F or change the name x".
>
> Another option to consider is not allowing private variables/methods to
> be accessed by friends. Only protected variables/methods. This allows
> inheritance to work as normal and guarantees overrides are compatible.
>
> — Rob
Honestly, this seems like the easiest model to explain to people. A friend
class has the same access as a child class, ie, it gets protected but not
private access. That's a very simple rule to document and explain, and seems
like it would side-step a number of issues. (We had a similar conversation for
aviz, IIRC, which is how we ended up on private => final and readonly =>
protected(set) simplifying a lot of issues.)
That said, I am still skeptical. This seems simple enough, but I worry that
it's still attacking the general problem in too specific a way (compared to
modules or similar).
--Larry Garfield