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