About them multi-paradigm programming languages

Милан Тодоров
3 min readJun 24, 2023

--

Some of these are: Java, C#, C++, Javascript, Typescript, Python. All of them are marketed as multi-paradigm. They “have” support for OOP, FP, procedural. What happens in reality is actually class-based, which is in its essence procedural in disguise. Long methods, ridden with conditionals, loops, multiple levels of nesting, christmas trees. Classes 500+ lines of code long, mostly singleton services, managed by a IOC container. You get the worst of both worlds — the bad performance of high-level languages and the bad maintainability of procedural languages.

How does the class-based procedural phenomenon happen

I think this is largely an issue of habits. When OOP was conceived, pure OOP languages (i.e. Smalltalk) received a lot of resistance from the status quo. Procedural programmers just didn’t want a radical change in the way they write programs. On the other hand they also didn’t want to continue creating the 1M LOC unmaintainable messes they used to. Under the pressure by procedural programmers, multi-paradigm languages started appearing. But when you are already used to procedural programming, which paradigm do you think you will use when given a multi-paradigm language? Right, you guessed it — procedural of course. You will just put your procedures in classes, making them mere bags of procedures. Then you will put your data in other classes without any behaviour, making them bags of data structures. Then you will throw the bags of data structures at the bags of procedures and will state “I’ve done OOP here”. Martin Fowler has written about this in Anemic Domain Model.

How to spot it

You see plenty of classes with names ending in Service or Manager, DTO. The former two are most used for the bags of procedures, and the latter for the bags of data structures. The first time I saw the Service thing, I remembered the book Domain Driven Design, where the chapter on Services starts with “Sometimes it just isn’t an object”, which is a procedural hint enough. Or maybe whoever created the class was reading a book about microservices, so decided to create something similar inside of a monolith, making these classes what — nanoservices?

How it should be, or at least how it feels right from an OO perspective

I feel the need to have an OOP language, where all the procedural operations (conditionals, loops, arithmetic) are removed. All of these can be responsibilities of objects. For example conditionals with two branches can be handled by Booleans (note the equals method below returns a Boolean):

Integer.new().value(5).equals(Integer.new().value(7))
.ifTrue(() => someObject.someMethod())
.ifFalse(() => someObject.someOtherMethod())

Loops can be handled by numbers or ranges. This will iterate 10 times:

Integer.new().value(0).to(Integer.new().value(9))
.do(() => someObject.someMethod())

Transforming these procedural operations to method calls is a great way to keep the developer disciplined about OOP and make them refrain from thinking in the old ways. Why, why can’t we have this?

About the new keyword

This is maybe the most misunderstood concept of OOP. It should be a method, not a language operation. In my early days, when I saw what Javascript was doing when you call new on a function (that’s right, it can be any function so you have to be cautious), I said to myself: WTF, don’t they know this comes from C++ where it means “allocate heap memory for the object being created and return the address”? Well, turns out I was wrong too. Years later I bumped into the Smalltalk world, where new is a class method for creating objects of that class. But it isn’t the only way to create objects. You can instead use withValue and provide an argument and so

Integer.new().value(0)

can be re-written like this:

Integer.withValue(0)

To summarize, the new keyword has to win the prize for the biggest bastardization of OOP concepts.

From a FP perspective

I have never written code in a pure FP language, and have never dove deep into FP as a paradigm. But something inside me tells me that a seasoned functional programmer will also not like what he finds in multi-paradigm languages.

Having worked several years with them, multi-paradigm programming languages feel like a bad idea now. Each project that I have seen feels so procedural and hardly maintainable. At some point every project gets so slowed down by the bad code organization and lack of maintainability, that adding new features takes ages and fixing bugs is a nightmare to secure against regressions. Maybe it would have been better to use a procedural language instead — at least we would have had good performance then. In the recent years, this exactly what is happening with Rust, by the way.

--

--

No responses yet