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.