1. Introduction
Today the most popular programming languages are based on object-oriented or functional paradigms.
In this tutorial, we’ll explore their characteristics and compare them.
2. Object-Oriented Programming
The most important building block that defines object-oriented languages is the object itself. Without objects, we can’t talk about object-orientation.
Object-orientation has multiple forms. For example, class-based inheritance (like Java or C#) or prototypal inheritance (like JavaScript). On top of that, objects embrace many core concepts. For us, the important one is encapsulation.
Encapsulation states that an object contains data and the operations, which work on the data. Moreover, in pure object-oriented languages, operations (functions) can’t exist outside of objects.
In short, objects focus on data. Operations only come after.
For example, Java is a pure object-oriented language. Meanwhile, JavaScript or C++ isn’t. Even lambda expressions and methods references didn’t change Java’s pure object-oriented nature since they’re just syntactic sugar to define classes.
Since primitive values aren’t objects, the presence of those makes Java impure in theory. However, getting rid of primitive values would make a language rather inconvenient.
3. Functional Programming
In functional programming, functions are the first-class-citizens. For example, they can be passed as a parameter for other functions, or we can store them in variables.
Functions get arguments, make operations on those, and usually return the result. Functions can also have side effects (e.g., modifying local or global state, performing I/O operations, etc.).
However, we prefer pure functions, which have the following two characteristics:
- for the same inputs, they always have the same output (therefore, they don’t depend on any state, just the inputs)
- they have no side effects
Because of these attributes, pure functions are deterministic, more testable, and much easier to use with other functions.
Note that a function without a return value is only useful if it has a side effect.
In theory, all of this sounds fine, but an application with only pure functions wouldn’t be of such use. The reason is that without side effects, it wouldn’t be able to produce any meaningful output because it requires I/O operations.
To sum up, functional programming focuses on operations rather than data.
Some examples of a functional language are Haskell, Scala, Clojure, or Erlang.
4. Comparison
Object-oriented and functional programming have different base concepts, but what does it mean for us? How different are they?
It turns out they’re very different. They’re as much different, as we can only compare them like we can compare two perpendicular lines. Indeed, we often say that they’re orthogonal paradigms. But what does it mean?
It means that they aren’t mutually exclusive. A functional language can have object-oriented characteristics, and an object-oriented language can have functional characteristics. We call these languages multi-paradigm languages.
Java, which is considered an object-oriented language, has the stream API, which helps functional style code. Likewise, C# has LINQ.
On the other hand, objects can be used as data structures in functional languages. On top of that, Scala even has classes.
Even if an OO language doesn’t have first-class support for functional programming, we can mimic it using immutable objects and model functions as worker objects with a single method.
Similarly, in functional languages, we can create objects, which operate on the encapsulated data. If the language allows variable reassignment, the objects can be mutable. Otherwise, we can only have immutable objects.
5. When to Use Which?
Since OO focuses on data, it makes a great candidate for data modeling. On the other hand, since functional programming focuses on operations, it should be used for processing data.
Indeed, Java 8 uses the same philosophy in its stream API. To model data, we use classes, therefore OO principles. To process that data, chain operations functional-style. What makes it more readable is defining what operations we want to do instead of how we want to do the processing.
6. What Paradigm Comes Next?
Robert C. Martin has a short chapter for paradigm overview in his great book, Clean Architecture. It summarizes structural, object-oriented, and functional programming in the following sentences:
Structure programming imposes discipline on direct transfer of control.
Object-oriented programming imposes discipline on indirect transfer of control.
Functional programming imposes discipline upon assignment.
After that, he writes the following:
Notice the pattern that I’ve quite deliberately set up in introducing these three programming paradigms: Each of the paradigms removes capabilities from the programmer. None of them adds new capabilities. Each imposes some kind of extra discipline that is negative in its intent. The paradigms tell us what not to do, more than they tell us what to do.
Another way to look at this issue is to recognize that each paradigm takes something away from us. The three paradigms together remove goto statements, function pointers, and assignment. Is there anything left to take away?
Probably not. Thus these three paradigms are likely to be the only three we will see – at least the only three that are negative. Further evidence that there are no more such paradigms is that they were all discovered within the ten years between 1958 and 1968. In the many decades that have followed, no new paradigms have been added.
7. Conclusion
In this short article, we saw that object-oriented and functional programming are not mutually exclusive. They both have their strengths, and by combining those, we can get a tool, which is better than both.