loading...

Truth about Functional Programming

codelitically_incorrect profile image codelitically_incorrect ・2 min read
(defun save-system (system)
           (let ((op 'asdf:monolithic-concatenate-source-op)
               (c (asdf:find-component system nil)))
           (asdf:operate op c)
           (asdf:output-file op c)))

There are 2 popular paradigms of programming for organizing code today. Object Oriented Programming and Functional Programming. Lately, more and more articles are springing up, pushing you to rethink objects (you know, the logical way to simulate real world people, places or things) and starting considering functional programming.

But you need to know the bias and the drawbacks. The bias is clear, it's an academic, geek-inspired, mathematical idea that is more whimsical than practical -- to show how uber and "Lispy" it looks, how smart one is at calculus, to adorn and lift up the ego about ones intellect.

That is the bias. An ugly splash of function calls embedded inside function calls, calling other functions and never destroying or creating state with side-effects, that is the benefit. An abstract, math-like mentality of algorithms dumped into your editor and executed in almost a primitive, cave-man style procedural flow. How horrible. We came a long way from that. Why not just go back to Assembly or C?

What is scary is that these geeks are pushing functional programming as the ideal paradigm for AI/Machine Learning. Reason about the devastation.

Object Oriented Programming is on the opposite side of the see-saw. It is a paradigm that organizes reality and thought-processes by simulating the real world using tangible objects. It's that clear. It's that logical. It models the world that we interact with. Objects are composed of objects, they communicate with each other, responsibilities are clear, dependencies are seen, system parts can be replaced, enhanced or debugged. There is nothing that cannot be simulated.

The drawbacks of functional programming is self evident:

  1. It's awkward
  2. mathematical
  3. almost procedural in nature
  4. ends up relying on objects eventually (contradiction of idea)
  5. a syntax nightmare
  6. harder to read
  7. difficult to debug
  8. complex to understand
  9. not natural

Be careful.

Posted on by:

Discussion

markdown guide
 

Firstly, why are you using Common Lisp to illustrate an article about Functional Programming?

CL is a perfect imperative language, and anything FP-related is quite awkward in it.

Secondly, both paradigms are not about "organising code", and there are far more than just these two, especially if you're talking about organising code in particular.

And, finally, no, OOP does not model the reality in a slightest. There are no hierarchical taxonomies of communicating objects in the real world. OOP is in no way less clumsy and unfit for representing real world problems than FP (which is, as you already mentioned, pretty much equivalent to it).

 

I'm all for not jumping into bandwagon but this looks more like a subjective opinion than truth.

 

The true lion has spoken, this is the end of functional programming era. In fact, i mark it as such, hold my words to it that you won't see or hear much about this in the coming years. It will be a thing of the past, a cliche, some whimsical idea, fantasy programming for math geeks.

 

To be honest, this article and this response read like the frustrated rant of someone who doesn't want to have to learn a different way of thinking.

Just to hold a mirror up: youtube.com/watch?v=XTLyXamRvk4

 

I have never ever, in my entire life... ever... seen an object-oriented codebase that models the domain by "simulating" real-world objects. It's all just a bunch of abstract stuff that has nothing to do with the real world. Instead of naming classes "User," "Form," or "Email" -- they tend to look more like "BaseUserDataModel," "FormInputGuardService," or "AbstractCacheManagerService."

It's no coincidence that classes always end up being divided into "data model" classes and "service/doer" classes. That's because, at the end of the day, we aren't working with real-world objects -- we are working with data, and database tables, and form inputs, and query parameters. And those "objects," so to speak, don't really have "behaviors." They aren't actors in an interaction between each other. They are just static data.

And at the heart of every business model is one very acute problem: the data we have doesn't yet look like the data we want. We have a user's name, but we want their email address. We have a database full of customer data and subscription plans, but the data we actually want is a table of billed accounts, so we can get paid. And how do we do this? By feeding the data we have into a process, a flow, which reads that data, calculates which actions to perform, and then executes those actions, giving us the data we want. This can be perfectly modeled using ordinary functions.

Here's what I've noticed. Every single sane OO codebase looks a lot like FP in terms of the architecture and design patterns they use. They use patterns like "event emitter," or "observable," or "pull-stream," or "mediator." All of those patterns are, at their core, about lining up a pipeline of functions to execute when you want to read/update state across your program. That's how NgRx works in the Angular framework. In fact, the various "RX" (reactive extensions) libraries you find in most OO languages are essentially just a super complicated way to implement FP patterns in a class-oriented, imperative paradigm.

To be very clear, these patterns fundamentally break encapsulation, but we do it anyway, because "encapsulation" was a false promise all along. It turns out that hiding your data in an object and letting the object be "responsible" for that data is just a nice way of sweeping complexity under the rug and hoping for the best. Nothing the object does with its data can be communicated to the world (unless you use the observable or event-emitter patterns), it's not expressed in the type system, nor does anyone know what happens to it, except for the implementer of the class... and even that person probably doesn't know how every state transition works in every situation in which an external object calls one or more of its methods.

In short, OOP is about hiding information from the programmer. This is often considered to be a good thing, since it reduces the mental overhead of solving high-level problems. BUT (and this is not OOP's fault, per se) hiding that information comes at a cost. In particular, the imperative and mutable nature of our code means that all kinds of things can happen under the hood without anyone knowing, and that is the fundamental reason that bugs exist.

In contrast, FP strives to empower the programmer to see all the information they care about, and also to removing the need to think about the entire program's state while solving an isolated problem. It effectively removes state from the equation. Yes, this comes at a "cost," you might say, since the only way to do "stateless programming" is to compose pipelines of data transformations. And not everyone knows how to do that right off the bat. But it's far from "academic." It's just different, and it solves a real world problem that most programmers have to deal with every day.

If there is one thing I would leave you with it's this:

The opposite of FP is not OOP, it's imperative programming. It just so happens that most OO-first languages are designed around imperative programming, and that's why FP always seems so foreign to OO programmers. It's that imperative, procedural aspect of the methods we write that makes our code brittle and requires merciless testing.