On Fri, Aug 8, 2025, at 15:11, Hans Krentel wrote:
> 
> 
> 
> On Friday 08 August 2025 00:49:27 (+02:00), Morgan wrote:
> 
> > On 2025-08-08 10:01, Rob Landers wrote:
> > > For example, if |A::foo(): int| promises to always return a non-zero 
> > > integer, and a subclass overrides |foo()| to only return zero, that 
> > > violates the contract—even though the type signatures match perfectly. 
> > > This is the sort of semantic guarantee that LSP is about, and it is 
> > > discussed in nearly every reputable textbook and conference talk on OO 
> > > design. Languages like PHP can’t enforce these contracts, but the / 
> > > principle/ is what helps prevent subtle design bugs and behavioural 
> > > “surprises.”
> > > Indeed. Those contracts only become enforceable in the type signature 
> > > when your type system is robust enough to be able to express them. If you 
> > > could declare |A::foo(): NonzeroInt| then the signature could prevent an 
> > > overriding subclass C from returning zero from that function (while still 
> > > allowing |C::foo(): PositiveInt|, say); the compiler can verify that the 
> > > type of the expression C::foo() is written to return is of type 
> > > NonzeroInt (or PositiveInt).
> > 
> > In the absence of being able to declare such constraints in the type 
> > system, one has to fall back on other documentation about what behaviour is 
> > and isn't acceptable.
> > 
> 
> That is certainly always useful, especially when it was written down, as it 
> allows to read both, the fine-print, and between the lines. As you have just 
> quoted Rob's suggestion while replying to it, allow me the opportunity to 
> highlight a key sentence for me under the pretext of the earlier quote, and 
> the much appreciated association with documentation of yours to illustrate 
> that:
> 
> >  [...] the / principle/ [here, LSP,] is what helps prevent subtle design 
> > bugs and behavioural “surprises.”
> 
> Documentation, e.g. of pre- and postconditions, loop (in)variants, class 
> invariants, etc, we can already make use of those corner-stones of the LSP to 
> reason about sub-types, the LSP can inspire us of what or how we document. 
> And that's what I've learned these days from Rob: Without reasoning about the 
> LSP. I knew already for what I love Liskov for, but the LSP is so much a 
> loaded thing in discussions, I had to get a couple of things out of the way 
> first to only understand Robs reasoning. I came here by the error message, 
> and was looking for what I was missing with it.
> 
> "Oh the types in PHP must do that for us, otherwise my scripts are doomed."
> 
> No.
> 
> The compiler can only handle the type but not the sub-type, because of the 
> halting problem.
> 
> And while my programs can be doomed because of the halting problem (4. Every 
> program is a part of some other program and rarely fits.), I as human am 
> still able to reason about the correctness of my program.
> 
> Just my 2 cents
> 
> -- hakre

To add to this, here’s one of my favorite examples, ironically, one often used 
in beginner OOP tutorials:

class Rectangle {
  public int $width;
  public int $height;
}

class Square extends Rectangle {
  public int $width { get => $this->width; set => $this->width = $this->height 
= $value; }
  public int $height { get => $this->height; set => $this->height = 
$this->width = $value; }
}

This is a classic LSP violation. Square changes Rectangle's contract by linking 
width/height, removing the independence that Rectangle promised. Meaning when 
we pass it as a Rectangle and we try to make the Square full screen, it will 
either be too short length (set width first) or too tall (set height first). In 
other words, a "Square is a Rectangle" violates LSP, in practice. Yet, this is 
quite often taught as a beginner example to OOP.

Are there cases where you'd want to do this deliberately? Yes, probably. Would 
you be surprised to find a rectangle extended to the edge of the screen did not 
extend to the edge of the screen? Yes, probably. In fact, it would probably be 
filed as a bug.

Interestingly, your product people will tell you to "stretch" it (or perform 
some other transform) making a Square behave like a Rectangle again. You'd 
probably end up with something more akin to this:

class Rectangle {
  public int $width;
  public int $height;
  public static function asSquare(int $widthAndHeight): static { /* set width 
and height */ }
}

Anyway, I digress. Software design is fun... it's a great time to be alive.

— Rob

Reply via email to