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.
Prerequisites
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 void
.
A function can extend another function, as can types. Inheritance is defined like so.
bar > baz(S) : T
T > S
In this example, baz
extends bar
, and 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.
Liskov substitution principle
Let's look at the official definition of the LSP.
If
S
is a subtype ofT
, then objects of typeT
may be replaced with objects of typeS
—Wikipedia
Instead of using S
and 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 Animal
or Cat
.
Let's say there's a function used to feed
an Organism
.
feed(Organism) : void
It must be possible to call it like so:
feed(Animal)
feed(Cat)
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 andfeed
thatOrganism
.
Because Animal
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 Organism
.Animal
may extend Organism
, meaning it may add functionality, but Animal
may not change the certainties given by Organism
.
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
Top comments (0)