DEV Community

Ben Halpern
Ben Halpern Subscriber

Posted on

OOP vs Functional Programming

Let's compare, contrast, and debate these programming paradigms. πŸ˜‡

Top comments (37)

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

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.

Collapse
 
daveparr profile image
Dave Parr • Edited

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?

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

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.

Collapse
 
rhymes profile image
rhymes

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

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

Nice synopsis! Head nods to all the goodies

Collapse
 
yaser profile image
Yaser Al-Najjar • Edited

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.

Collapse
 
rrampage profile image
Raunak Ramakrishnan

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.

Collapse
 
kant312 profile image
Quentin Delcourt

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.

Collapse
 
tvanantwerp profile image
Tom VanAntwerp

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. Β―\_(ツ)_/Β―

Collapse
 
elliot profile image
Elliot

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 :)

Collapse
 
patryktech profile image
Patryk

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.

Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

@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?"

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

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.

Collapse
 
dc0d profile image
Kaveh Shahbazian

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.

Collapse
 
rfaulhaber profile image
Ryan Faulhaber

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.

Collapse
 
_hs_ profile image
HS

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.

Collapse
 
saint4eva profile image
saint4eva

C# and Java are very powerful and popular programming languages

Collapse
 
rhymes profile image
rhymes

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

Collapse
 
leob profile image
leob

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".

Collapse
 
macsikora profile image
Pragmatic Maciej

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.

Collapse
 
leob profile image
leob • Edited

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.