I've been fascinated by type systems in programming languages for a while now. Recently, something clicked for me about inheritance and types.
Not only did it clarify type variance, I also understood what the Liskov substitution principle actually is about. Today, I'm going to share these insights with you.
I'll be writing pseudo code to make clear what I'm talking about. So let's make sure you know what the syntax of this pseudo code will be.
A function is defined like so.
foo(T) : void bar(S) : T
First comes the function name, second the argument list with types as parameters, and finally the return type. When a function returns nothing, it's indicated as
A function can extend another function, as can types. Inheritance is defined like so.
bar > baz(S) : T T > S
In this example,
S is a subtype of
T. The last step is being able to invoke the function, which is done like so.
foo(T) a = bar(S)
Once again: it's all pseudo code and I'll use it to show what types are, how they can and cannot be defined in combination with inheritance, and how this results in type-safe systems.
Let's look at the official definition of the LSP.
Sis a subtype of
T, then objects of type
Tmay be replaced with objects of type
Instead of using
T, I'll be using more concrete types in my examples.
Organism > Animal > Cat
These are the three types we'll be working with. Liskov tells us that wherever objects of type
Organism appear in our code, they must be replaceable by subtypes like
Let's say there's a function used to
feed(Organism) : void
It must be possible to call it like so:
Try to think of function definition as a contract, a promise; for the programmer to be used. The contract states:
Given an object of the type
Organism, I'll be able to execute and
Cat are subtypes of
Organism, the LSP states that this function should also work when one of these subtypes are used.
This brings us to one of the key properties of inheritance. If Liskov states that objects of type
Organism must be replaceable by objects of type
Animal, it means that
Animal may not change the expectations we have of
Animal may extend
Organism, meaning it may add functionality, but
Animal may not change the certainties given by
This is where many OO programmers make mistakes. They see inheritance more like "re-using parts of the parent type, and overriding other parts in the sub-type", rather than extending the behaviour defined by its parent. This is what the LSP guards against.
Continue reading on https://stitcher.io/blog/liskov-and-type-safety#benefits-of-the-lsp