On Thursday 07 August 2025 08:46:26 (+02:00), Rob Landers wrote:

> 
> 
> On Thu, Aug 7, 2025, at 00:38, Hans Krentel wrote:
> > 
> > 
> > 
> > On Wednesday 06 August 2025 13:41:14 (+02:00), Rob Landers wrote:
> > 
> > > 
> > > 
> > > On Wed, Aug 6, 2025, at 13:26, Hans Krentel wrote:
> > > > 
> > > > 
> > > > 
> > > > On Sunday 03 August 2025 11:30:13 (+02:00), Rob Landers wrote:
> > > > 
> > > > > I'm not sure that this is a bug. You can redeclare the same type and 
> > add 
> > > > hooks (or change them), which breaks all assumptions about 
> > > > substitutability.
> > > > 
> > > > Rob, maybe you can lecture me a bit: Isn't substitutability on the 
> > public 
> > > > interface / protocol only? What am I not seeing in your example?
> > > 
> > > From a child class's perspective, the "public interface" is both 
> > protected + public of the parent. If you change or misuse a parent's 
> > implementation or invariants, it violates LSP, even if it doesn't affect 
> > external clients immediately.
> > 
> > Ah okay, that part is not in my book, this explains to me why in your 
> > example it violates substitutability for you, and with that thinking it 
> > also prevents or degrades implementability for me so to say, as otherwise I 
> > could not make use of visibility in classes - it would take away that tool 
> > from me or I would not treat it well, potentially leading to defects in the 
> > program.
> 
> See my reply to Jonathan. But you are free to dismiss LSP when needed. There 
> are a lot of times when LSP isn’t the right design constraint (which I 
> briefly mention in that email), for example, during larger 
> migration/refactors, specialized proxies, caching results, etc., or even 
> using sibling classes as friend classes.
> 
> PHP doesn’t strictly enforce LSP everywhere. It will get out of your way when 
> you need it to. It’s your code, you can do whatever you want with it. 
> 
> Even when I see an LSP violation at work (rare, but it happens), I don’t 
> point it out as such, but instead point out why the approach is a bad idea 
> (maintainability, principle of least surprise, etc). If they do it 
> continuously, then I might have to invest in some coaching for the dev, but 
> mostly, people don’t violate LSP for the more obvious reasons, and when they 
> do, they usually have good reasons (see above).

Thanks a lot for sharing your thoughts that openly and briefly, much 
appreceated.

This all sounds rather sane to me, given PHP is not a design by contract 
language and what makes LSP stand out from formal type theory can get easily 
lost then, especially when applied with force.

I've also studied your other reply to Jonathan.

> 
> > 
> > >
> > > Take for example:
> > > 
> > > class Fruit {
> > >   public string $kind { get => "fruit" }
> > > }
> > > 
> > > class Vegetable extends Fruit {
> > >   public string $kind { get => "vegetable" }
> > > }
> > > 
> > > function foo(Fruit $fruit) {}
> > > 
> > > foo(new Vegetable); // hmmm
> > > 
> > > This is a "soft" violation that only makes sense to us humans, but PHP 
> > allows it. It requires us humans to realize we are performing an LSP 
> > violation and refactor the code so that we don't pass a Carrot to someone 
> > expecting a Mango.
> > 
> > Thankfully in this example it is all public, but I definitely would say 
> > this is not an LSP violation, just saying.
> > 
> > >
> > > This can be done through protected means as well (simply replace the 
> > properties above as protected properties used internally), and it won't be 
> > as obvious to consumers outside the class, but still there, nonetheless.
> > > 
> > 
> > Okay, this is it probably just like above (for me): When $kind would be 
> > protected, it would not be part of the public protocol, and the 
> > substitutability test with the PHP runtime would still pass for the 
> > foo(Fruit) event with a Vegetable that is a Fruit (extends). That would be 
> > a test for substitutability, per the PHP runtime guarantees (it returns 
> > successfully after sending the message), it does not break the program:
> > 
> > >> an object (such as a class) may be replaced by a sub-object (such as a 
> > class that extends the first class) without breaking the program. (WP LSP) 
> > <<
> > 
> > Still trying to learn more, though.
> > 
> > Let me guess: The following hierarchy is not substitutable for you, as we 
> > can still pass Vegetable for Fruit on foo()'s protocol. Is that correct?
> > 
> > 
> >      class Fruit {
> >          // intentionally left blank
> >      }
> > 
> >      class Vegetable extends Fruit {
> >          // intentionally left blank
> >      }
> > 
> >      function foo(Fruit $fruit) {}
> > 
> >      foo(new Vegetable); // hmmm
> > 
> > 
> > -- hakre
> 
> I don’t see any reason why this example would violate LSP. There is no 
> discernible difference between the two classes. LSP only says they are 
> substituable in regards to type, and behaviour, and an empty class is 
> probably only a sentinel value, in which case the behaviour is external to 
> the type. I might have an issue with saying a vegetable is a fruit, but that 
> is a naming issue… and naming is hard. 

So by protocol, this settled a bit more in harmony. Fine.

And while naming is hard, names are harder: Given that LSP, PHP and the 
(earlier with hooks) class definitions are all human made, and furthermore that 
fruits and vegetables grow from nature, there is or was at least one local 
jurisdiction that would have needed them in their program exactly that way: a 
vegetable declared Fruit. A delicious example of sub-typing refreshingly 
POLArizing.

Thanks Rob.

-- hakre

Reply via email to