Functional Programming and the Origin of State
I’ve been learning Elixir in my spare time, and it’s really a wonderful language to work with. It’s also a great introduction to anyone who wants to learn functional programming and it’s taught me quite a few things about it’s cousin, object-oriented programming.
Object-oriented programming is a programming paradigm that has been around for decades. It’s strengths lie in the ability to group like variables and functions together into a logical “object”. This sort of grouping allows us to create useful libraries grouped around important ideas, concepts, or tools. There’s also the three pillars of OOP: encapsulation, inheritance, polymorphism. Encapsulation is the grouping of like variables & functions and abstracting part of OOP, inheritance allows us to use those abstractions we create (think TaskChair : Chair
) , and polymorphism allows objects to appear in more than one form (sort of like how IEnumerables
in C# have common methods available to them, interfaces as declaration classes as implementation, etc.). Now these are all elementary OOP ideas, and anyone who’s had any sort of experience working on a medium-to-large project will be familiar with them. However, a talk by “Uncle” Bob Martin (found here) got me thinking that I should probably learn a functional language.
In his talk, “Uncle” Bob goes over why object-oriented programming is not going to be able to carry us much further in computer science, and it comes down to how close we are bumping up against the outside edge of Moore’s Law. Processors haven’t advanced much in the past few years in terms of raw clock speed, and we’ve had to compensate a lot by bettering our dynamic overclocking methods. In fact, Martin remarks that 2.6 GHz is basically all we got in terms of raw clock speed and that our processors will have to cram more cores onto the die for them to get faster. As a consequence of this, programmers are going to have to get better at concurrency in their programming. Functional programming offers an answer to the problem of multi-threaded applications changing shared state at inopportune moments. Unfortunately this kind of paradigm comes with a cost that not many people talk about: memory.
The most common reason you will find for programmers to reach for a functional language is the nastiness of side-effects. What’s often not talked about is the nastiness of memory performance in a functional language. Every method call in a functional programming language passes in parameters by value instead of by reference. Sure this means that state is never shared, but sometimes you don’t want to make a copy of everything. This could be a big problem in memory-constrained applications, and memory constraints, functional programming, and IoT devices would otherwise go hand-in-hand were it not for this fatal flaw. Now, most of the time we’re talking <256mb of RAM. Not a big deal in most cases, but if a)your program is complex and resource-intensive, b)your program is lazily coded, or c)you don’t take your resource-intensive application and offload to the CPU cache then you’re probably going to have a bad time. Most people won’t run into this using Elixir, however, since it uses Erlang’s process management engine to do garbage collection and process supervision.
We’re getting into the weeds a little bit, and I’d like to lay off of Elixir and C#/Java a little bit, and explain use cases for both kinds of programming. Memory management and concurrency has mostly been abstracted away for all but the most resource-intensive applications. OOP languages like Java, C#, and to an extent, JavaScript have methods of keeping state separate or safe during concurrent operations; async-await
in C#, promises in JS, java.util.concurrent
, etc. So why the need for functional programming over an object-oriented language? I can’t answer that question definitively. OOP languages are good at code reuse, most of them were architected with reuse in mind. Functional languages, particularly Erlang, was built for distributed systems first. So which one is better? What makes it better to use one paradigm over the other? I think that the fundamental difference between OOP and functional programming paradigms is in their answer to the question which came first, the chicken (function) or the egg (variable)?
Object-oriented concepts are often described using real world objects and the one I’m most familiar with is the Chair : TaskChair/Table : KitchenTable
model. In this model, we have an abstract class of Chair with a Task Chair class that inherits from Chair. This task chair has a color attribute or property that is instantiated to black when the constructor is called.
Seems reasonable, but if you take a look you’ll see that this assignment happens inside of a constructor method. Methods are basically functions that are members of certain classes, and functional programming has those. Why do we need objects again? Even if we put an assignment before the object is instantiated, we are likely to modify state within the class. The point is that even in OOP we require action to modify or create state, and action is what functional programming is all about. This is also congruous with the real world in that all life is predicated upon action.