Learn the true polymorphism
Polymorphism is the ability of an object to take on many forms. It is an everyday word in Object-Oriented Programming (OOP). We have classes that inherit from other classes. We can treat an instance of a child class as an instance of the parent class, but it will behave differently.
Common OOP languages have a very limited version of polymorphism. In this post, I'm going to talk about a wider version of polymorphism that is included in Lisp.
A first example
Let's see first what can be done with the polymorphism we commonly use.
As a simple example, we can have a class called
Shape with a method called
Area. We also can have other classes called
Circle, etc. Those classes inherit from
Shape, and each one provides its own implementation of the
We can make a list that only contains shape instances and treats every element as an instance of the
Shape class. But when we do
element.Area() we get different implementations every time. No matter that we treat every element as a
Shape instance, every one of them behaves as a
Circle, or a
Rectangle according to its class.
That's the kind of polymorphism you might use in your favorite OOP language. But what if I tell you that's a very limited kind of polymorphism?
In the previous example, we had a method inside a class that got overridden by every child class. That was good because we wanted to have different implementations of the
Area method for every shape.
Let's think about another example. Now we have a class
Drum, and also let's create the class
Stick. Where do you include the
Play method? Inside the
Drum class? Inside the
If you choose to include it inside
Drum, then you can have different implementations of
Play for every different drum you create. But what happens when you have a different stick? You can't have polymorphism with respect to the
Stick instance! Exactly the opposite happens if you decide to include the method inside the
Stick class: you'll have polymorphism on the
Stick instances but not on the drums.
Where's your polymorphism now?!
Taking a deeper look
Let's think about this issue. What happens when you do
We can say we are invoking the
ClassMethod inside the
classInstance. But let's change the previous code a little bit:
Now we see things slightly different. We are invoking the
ClassMethod passing the
classInstance as the first argument.
Maybe this is familiar to you. For example, in Python, you can invoke class methods using both ways interchangeably. Both of them are equivalent.
Using the second way, we can achieve different
ClassMethod behavior by changing the
classInstance argument. That's equivalent to polymorphism we use every day. That's called polymorphism in the first argument.
Let's return to our "drum and stick" example. The solution would be having something like this:
Play(drumInstance, stickInstance, <args>)
Now we can have different behavior for every Drum-Stick combination. Nice!
But that's not a natural solution in many programming languages. Usually, every method has the "special" first parameter reserved for the instance of the class where it is defined. Hence, we can't have many "special" parameters representing many different instances of different classes. Even more, many OOP languages don't allow you to define methods outside of a class. So yes, you probably have been using a very limited version of polymorphism during your coding life.
Let's see what Lisp has to say!
The Lisp solution
Lisp is a very old programming language. It's so old that there was no OOP when it was created. But in the '80s a new update of the language came up with the Common Lisp Object System (CLOS), And then, Lisp became an OOP language.
As a matter of fact, CLOS is implemented in Lisp itself. There are a lot of great things about Lisp. I will definitely write about more of those things shortly.
There are no class methods in Lisp. Methods don't belong to the class. You have classes with properties and methods that can receive instances of those classes as arguments. It's the solution we talked about! That's how Lisp provides us with polymorphism on multiple arguments.
Of course, you can solve all kinds of problems with the "regular" polymorphism. You also can solve all kinds of problems without OOP at all! You can think about solving the Drum-Stick problem with a design pattern or some abstraction. But polymorphism in multiple arguments is explicit and simple.
Lisp's CLOS has a lot of more interesting features. This was the first one that really impressed me. Also was the first one I exploited a lot for solving some problems. Talking about other features of CLOS requires some prior knowledge of the language, but I plan to write about topics that don't demand such knowledge. Like this one!
Let's summarize what we have been talking about.
Polymorphism is a cool feature in OOP. It is the ability of classes to behave differently, in different scenarios. But the kind of polymorphism that is present in popular OOP languages is limited.
Yes, there are different types of polymorphisms. We commonly use polymorphism on the first argument, but we can have polymorphism on multiple arguments. To achieve that, we need to separate methods from classes.
Lisp is a multiparadigm (Object-Oriented included) programming language. In Lisp, methods are created outside classes, and we can have polymorphism on multiple arguments.
Using this more general polymorphism you can get rid of some abstractions and design patterns that maybe you have been using to solve problems similar to the Drum-Stick one.
There are a lot of other features about Lisp that are mind-blowing. It really changes the way you think about programming. I plan to write about some of those features with a focus on the change of paradigm instead of the language.
But I recommend you to take a look at Lisp, and to experience those features and different ways of thinking by yourself.
If you liked the post let me know. You can leave a comment with whatever you want to say. You can also follow me on Twitter for debates and thoughts about related topics.