loading...

OOP vs Functional Programming

ben profile image Ben Halpern ・1 min read

Let's compare, contrast, and debate these programming paradigms. 😇

Discussion

pic
Editor guide
 

I think there are nuances within each.

  • Static OOP - so much ceremonial overhead to create things, but able to optimize perf
  • Dynamic OOP - minimal ceremony. but OOP still has challenges. inheritance models make it easy to arrive at awkward abstractions. mutable statement-based syntax naturally leads to challenges in testing/maintainability. takes a while to learn all the practices to make OOP nice.
  • Static FP with monads - advanced math and inscrutable symbols for maximum conciseness and minimum readability to outsiders. takes a while to learn how to use it.
  • Lisp Dynamic FP (Clojure) - idea-wise, I like it the best (the language is also the compiler). but hard to get into. relies on tooling a bit. common coding styles seem overly optimized for execution to detriment of human readability.
  • Static plain FP - almost feels dynamic with type inference. can optimize perf. separating data and functions makes composability and reuse more plausible than with objects. some extra boilerplate w/o advanced type/compiler features.

Static plain FP is my preference. It is highly maintainable when you use immutability and deterministic decisions. If you are are new to programming it is easiest to learn among these, based on my experience training new devs. However if you are already accustomed to statements / mutability, it can be a challenge to transition to using immutability and deterministic decisions.

You can get work done in any of them. And with enough experience or investment, you can even manage to avoid most of the pitfalls mentioned.

 

Neat breakdown. Within the criteria listed above, where would you put R? It is now very common to write 'tidy' R code, which stylistically pipes with the %>%. This, as far as I understand monads, seems to represent the monadic axioms. It however is not static but dynamic (as far as I understand those terms to refer to typing). It's also not a LISP, but derived from a pretty odd language in itself called S. It's also not immutable, however it encourages currying by default.

Personally, it's obviously my bag, but how to the rest of you see it fitting in above?

 

I'm not too familiar with R, so I won't attempt to declare if/which category it might fall into. (I only went over FP/OO approaches.) But you bring up an interesting point with the pipe operator. A lot of the constructs we use in programming can be proven from category theory or other area of math. But this isn't exactly what I meant by FP with monads.

When I mentioned FP with monads, I was referring to code bases that use named category theory abstractions like Monad, Monoid, Semigroup, etc. The value proposition is enticing... instead of arbitrary abstractions (custom objects), use these known abstractions with mathematically provable properties/behaviors. And build the application up from those. Theoretically, every time someone creates a new custom type of object, you have to learn its custom rules. But basing the code on category theory abstractions, you can learn a single set of math-based objects. Unfortunately to contribute to or even read such code, a dev has to backfill a lot of knowledge from advanced math. Which is a larger barrier than understanding some custom objects. And the interactions of different category theory constructs are many and varied. So the category theory stuff doesn't turn out to be a small set of things to learn, either.

 

Static plain FP - almost feels dynamic with type inference. can optimize perf. separating data and functions makes composability and reuse more plausible than with objects. some extra boilerplate w/o advanced type/compiler features.

yeah! :D

my favorite too

 

Nice synopsis! Head nods to all the goodies

 

OOP:

A fancy way of programming to represent real aspects of an entity in programming, say a "car":

car-lynda-object
src: lynda youtube.com/watch?v=NUl8lcbeN2Y

And yes, everything referenced gets changed when it's passed by reference, unless you pass it by value... cuz it follows the imperative programming paradigm.

def my_function(car):
    car.name = 'Hyundai' # changes the original object name

c = Car(name='Kia')
my_function(c)
print(c.name) # prints > Hyundai

Functional programming:

A safer approach than OOP that is a bit related to math (its origins is from the lambda in calculus).

They key here is "immutability", where every function should create a new object, and not change (mutate) the passed object... aka "declarative".

const numList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const result = numList
               .filter(n => n % 2 === 0)  // here a new object gets created
               .map(a => a * 10)             // and another one here
               .reduce((a, b) => a + b)   // and another

That being said, here we have created 4 different objects.

Is any paradigm better than the other?

No, just use the right paradigm in the right context, it is very common to write OOP in Python and to write functional in JavaScript.

 

FP and OOP mean a lot of different things to different people. This is something to keep in mind while discussing.

Here's why I like FP:

  • Immutable data structures
  • Pure functions (without side-effects) which can be easily tested
  • Building complex software by composing functions
  • Making illegal states unrepresentable using the type system
  • Algebraic data types and pattern matching

There are different types of languages in FP. For example Haskell is lazy by default and this allows you to easily construct some types of programs but makes it much harder to reason about performance. OTOH, Ocaml is an eagerly evaluated language with not as powerful type system as Haskell but it allows to write programs which are competitive in performance with Java and Go.

OOP as envisioned by Alan Kay involves message passing and lightweight programs (like in Smalltalk). What we use in Java and C++ is quite different from his original ideas. That said, I have heard that OOP is useful in GUI programming. Languages like Java and C# have many good properties which make it easier for building large projects with large teams. There are design patterns to efficiently express common idioms.

FP, OOP, Imperative programming, logic programming all have their place in a programmer's toolbelt. Some concepts lend themselves to using one or the other. We should view them as tools to be used as required and not as the one true way of doing things.

 

Great answer. I second every statement. Very important for those interested in the history and understanding of the different paradigms are the people behind these: Alan Kay as a prominent example of where "OOP" originated (a term that Alan himself has never been too happy with).

 

The thing I love about JavaScript is how easy it is for me to have a bastardized combination of OOP and functional.

Yes, I'm an awful person. ¯\_(ツ)_/¯

 

Honestly I really like this about JavaScript as well! You get some good parts of OOP and the best parts of FP and the worst parts from both :)

 

I routinely mix both styles in Python as well.

If you look at the way the language is designed, I think it makes a lot of sense. Iterators, map function, filters, generators... They mesh really well with classes.

 

OOP ♥️ FP
I try to apply both at the same time. Objects should be made immutable and methods should have ideally no side effect, when possible.
The combination is for me an incredible and efficient way to model business logic.
Putting one against the other would be missing out on important concepts, imho.

 

But immutable objects are very much the opposite of what OOP is about (encapsulating state), or am I misunderstanding your approach?

I understand the benefits of side-effects-free programming, but those side effects are an inherent part of OOP.

 

To me OOP is not about encapsulating state (although you can do that of course), but rather encapsulating data with behaviour. The fact that these data should be immutable or not depend on the software needs, but I think most of the time we should err on the side of immutability.
TL;DR, I don't think using OOP should force you to have mutability everywhere.

Where's the difference between state and data? I think we are talking about the same thing here.

No, OOP doesn't force you to have mutability. Rather, OOP emerged to satisfy the need for managing mutable state carefully.

 

@avdi gave a good talk relating to some of these ideas at RubyConf in Nashville.

I'm not functional aficionado, but I tend to think OOP and FP are not mutually exclusive. A good example, in my opinion, is the Actor Model which (as I understand it) fits into both camps.

From the sidelines of this conversation, I kinda wonder, "why not both?"

 

A traditional Actor is almost definitionally an object with private state and public behavior via messages. But to your point, there are functional adaptations which, instead of having private state, are provided their previous state and a new message to process, then return a new state and side effects to execute. MVU update function is exactly this from a dev perspective. Although from a runtime perspective it is usually still hosted inside a traditional actor/object. Because of our heritage from the von Neumann architecture at a low level.

 

I apologize in advance for my rambling response.

I feel like any discussion of OOP has to be qualified to some degree, because often when I hear "OOP" they're usually talking about writing code using Java or C#. So although this is kind of a strawman, I think that the OOP of Java and C# is incredibly rigid and antiquated, and I don't think it's a coincidence that modern languages have abandoned a lot of features from these languages, namely inheritance (and Java even has lambda functions now! Quelle horreur!). These languages also promised far more than they delivered, and in languages like these, a more "object-oriented" solution would be far more contrived when a single or couple of functions would suffice.

But I don't think OOP is all bad! Things like interfaces are great in statically typed languages, and they make languages like Go and Rust very generic. I do think Smalltalk does OOP better, though, and is what OOP should really strive to be.

I think functional programming is hard, and that's its biggest con. At least given the way I was educated in computer science, there's no "easy" approach to learning functional programming. That said I have gotten much much more mileage out of code that is more functional than object-oriented. It's way easier to write generic, simple, and testable code when your code is just a bunch of simple functions that you can compose and build your way up to more complicated behavior.

I think languages like Rust or Go strike a good balance for most uses: static typing with functional features. Sometimes you need stateful objects, and so you can write a struct with methods, but you're not obligated to (in fact, when it comes to a language like JavaScript, the language I write most, that's my rule of thumb: do I need state? if yes, I'll write a class, if not, it's a function. Most of my code lives in functions).

Finally, I do think functional programming is wildly underappreciated and has a lot more to offer than people give it credit for. Immutability has saved my skin more times than I can count, for example. I think a lot of languages could benefit from adding more functional aspects, honestly.

 

We'll let's just say OOP as in Java and C# an take it from there. Even though Alan Kay did say he regrets calling it OO and wanted something like message driven and ended up with some explenation of Actors ppl call OO anything with classes. Late Armstrong did say Erlang was OO more than others and quoted Kay but people that use Erlang will smash your head if you call it OO.

 

C# and Java are very powerful and popular programming languages

 

Let's assume that we can question everything that is assumed to be a fact (or ask stupid questions - your take).

OOP was about message passing

Apparently, the language that is most close to the original idea of OOP, is ... Erlang! According to Alan Kay (mentioned this on different occassions).

Closures & Functional Programming

Here we are cheating a bit, just to communicate better. We can blame things after.

const (
    factor = 10
)

func add10(n int) int      { return n + factor }
func multiply10(n int) int { return n * factor }

Here, only functions are used. The factor constant, is a closure, accessed inside add10 and multiply10 functions. Very Functional so far.

Factor Out The Shared Closure

An idea: let's factor out the shared closure and put it inside something (we will call it an object later).

type factor struct {
    value int
}

func newFactor() factor {
    return factor{value: 10}
}

func (f factor) add10(n int) int      { return n + f.value }
func (f factor) multiply10(n int) int { return n * f.value }

Wow! An Object is very much like a bunch of functions with a shared closure!

Last Bit, Mutation

type factor struct {
    value int
}

func newFactor() factor {
    return factor{value: 10}
}

func (f factor) add10(n int) int                           { return n + f.value }
func (f factor) multiply10(n int) int                      { return n * f.value }
func (f *factor) makeOtherMethodsMeaningless(newFactor int) { f.value = newFactor }

Now we have a mutable closure, shared between a bunch of functions (now called methods).

OOP and Functional Duality

OOP and Functional Programming "can" be seen as a dual. They represent the same thing, to a great extent and they try to provide tools to solve the same problem: composition.

What about message passing?

We simply forgot about that - in time. OOP - in its original form - was about message-passing and behavior. And was trying to organize state and transformation through composing behaviors. While the OOP that we have tries to solve the composition problem, through data structures/type - same as Functional Programming.

Behavior has been forgotten.

This was a quick sketch for drafting out these ideas and get them written. Maybe it can be improved, maybe not.

 

This is what I have answered myself after listening to a lot of lectures on this topic:

OOP tries to bring the "real world" to you. The real world which is ugly, ruthless but something you can easily relate to. Everything is affected by time (state) so you better manage it correctly.

FP tries to bring the world you fantasize about. The world it presents is beautiful, ideal. Things go the way how you want them to. Nothing here is affected by time because there's no state. But it hides the ugly reality from you and there is a cost for that.

Some prefer the real ruthless world, some prefer the fantasy. Although it's possible to have both depending on the situation when you are in the real world, but not the other way around.

 

Interesting how some people (years ago) predicted FP to become "the next big thing" but we're all still waiting for it to really take off. Frameworks like React have a bit of an FP feel and replacing loops with map/filter/reduce in Javascript have become somewhat popular, but the top 20 "most popular" programming languages doesn't contain even one FP language.

I'd be open to embracing FP and go all the way with it, at least for some projects, but it looks like it ain't happening yet (if ever). Still waiting for FP's "big moment", after all those years it's still "niche".

 

20 most popular languages are mostly multi-paradigm 20+ years old like Java, C, C++, JavaScript., Python, PHP. Till 2010 there was no hype for FP, therefore languages FP features were even not discussed, but they were there. JavaScript for instance was always lamda language, there were no classes, you had higher order functions and closures from beginning.

The current state of art is that every of mainstream languages has FP features, even Java considered as strictly class OOP language has for example lamdas/closures and Optional. C# language query LINQ was inspired by Haskell and static type functional languages. Promise in JavaScript is monadish and inspired by FP. Array, List structures almost in every language have higher order functions like .map, .filter and .reduce.

From the new languages. There is no new language I am aware of which doesn't have FP features. Take Rust as an example. Rust has lamdas, higher order functions, Optional and for example Vector in Rust has a lot of similarities to List in Haskell. You can write FP code in Rust and even it is hard to not.

Pure FP is still a niche. So in terms of what is not happening, it is popularity of pure FP. So languages without side effects like Haskell or Elm. I feel here the learning curve and the difference in programming is huge, that is why it is hard to grasp concepts from such languages. IMHO expression based languages are the future, but I am not sure if full purity is, maybe algebraic effects will change that. Will see.

 

Rust is very interesting, I think among the "popular" languages it comes closest to being an "FP light" language. I've studied it a bit and it reminded me a lot of Haskell with its algebraic data types.

Of the major "popular" languages I guess that Javascript has the most FP potential.

However if I'm not mistaken you could say that Go is an outlier, the fact that it doesn't support generics mean that map/filter/reduce are as good as impossible, you're more or less forced to program everything with procedural loops.

But for the rest, if you look at the top 20 languages what surprises me is how 'conservative' it is, people stick to what they know, ancient C and C++ are still high up there, really innovative and ground-breaking languages aren't making it into the top 20.

I know that some FP features are making it into most popular languages, Javascript is a language with a good deal of that, however only a pure FP language will force you to think with a genuine FP mindset, barring that people will adopt some of the map/filter/reduce style but that's about it.

In other words, regarding FP breaking into the "main stream" I'm not holding my breath.

 

Both.

Though the more time I spend programming, the more I prefer what @kspeakman described as "Static plain FP".

If I look at my Python code over the years, in the last few of them most of the code is functions receiving values and returning other values. There are side effects (like I/O) but there's little to no shared state.
They sit in modules (which are slightly different concept than Ruby modules) and that's it.

Everything in Python is an object (functions and modules too) which makes it easier to use dependency injection to pass around things.

I would call this style: functional programming on top of object oriented programming.

Hence both :-D

 

OOP for data and overriding/patterned business logic. Functional for actual business logic implementation.
So, business logic is implemented in objects, but its methods are functions.

You use them both.

 

I'd like to quote Michael Feathers, who made a brilliant remark on Twitter:

"OO[P] makes code understandable by encapsulating moving parts. FP makes code understandable by minimizing moving parts."

 

Err on the FP side, and then encapsulate your data-structures with OOP. Why not both?

 
 

Yes...just like Rust! RIIR

 

My thoughts summed up super concisely. 👍

 

I think it's purely contextual. When building an application sometimes you have series of things that you would like to have happen (Scripting). Other times you have entities/actors (Objects). Like most things, knowing what pattern to apply at what time can make more of a difference than how well they're applied.

 

I am a big fan of functional programming. I really like the safety that I get from immutability, pure functions and I find function composition natural.

However, I usually go for a hybrid approach (oop+fp) applying the correct paradigm where it makes sense. However, the language also plays a crucial part in it (In js, I usually lean towards FP, whereas in languages like PHP I usually go with OOP first approach.)

 

Scala hehe. Both, as much as possible, and choose what's right for the specific problem couple of lines at a time.

Map, filter, immutable data, ... many of those are treated as functional benefits but more and more OO languages have them now as crucial part of the languahe syntax. They make an effort to make it as nice and as easy as possible to use these in OO. And Scala was first mainstream lang that made it.

 

I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus >on the lesser idea. The big idea is “messaging” - Alan Kay, father of OOP.

To me, a wrong OOP design can make everything worse.
FP somehow it's easier, you just need to have a good function

 

Does it really matter what paradigm you are a disciple of? I am interested in passionate programmers who know their craft and from whom I can learn something.

 

OOP vs FP
Do you think the programming paradigm you use effect your daily life, like in psychological way. The way you think, plan and everything.
This may be an interesting view point too I feel.

 

What's an object?

 

OOP and FP complementing each other. So "vs" is not good fit here.

 

OFP because programming is not black and white.

 

I use OFP because why not.