DEV Community

Discussion on: Truth about Functional Programming

Collapse
 
austindd profile image
Austin • Edited

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.