DEV Community

Brent Roose
Brent Roose

Posted on • Originally published at stitcher.io on

Liskov and type safety

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 of T, then objects of type T may be replaced with objects of type S

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 and feed that Organism.

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)