
OOP vs. Functional Programming (2014) - ruairidhwm
https://blog.cleancoder.com/uncle-bob/2014/11/24/FPvsOO.html
======
lmm
> Objects are not data structures. Objects may use data structures; but the
> manner in which those data structures are used or contained is hidden. This
> is why data fields are private. From the outside looking in you cannot see
> any state. All you can see are functions. Therefore Objects are about
> functions not about state.

The statements are correct but the conclusion is backwards. The defining
characteristic of OOP is that hidden state. Passing functions around is done
all the time in functional languages.

> The bottom, bottom line here is simply this. OO programming is good, when
> you know what it is.

Ehhhh nope. The idea that you need "function pointers" to do polymorphism is
confusing one particular implementation technique in one particular (frankly,
bad) language for some universal rule. You can do polymorphism perfectly well
without needing hidden mutable state - and when you do, life is generally
better.

~~~
danharaj
> The defining characteristic of OOP is that hidden state.

Hidden state is the characteristic of abstraction, period. OOP has no special
claim to it.

~~~
lmm
Nonsense. Most abstraction isn't about state at all (you won't find state
anywhere in the definition of a group, say). You can of course abstract over
the difference between having state and not having it (as OO does), but that's
a virtually useless abstraction because it doesn't preserve any nontrivial
rules/symmetries.

~~~
thrmsforbfast
Perhaps a better way of saying is is that hidden _data_ is a characteristic of
abstraction. Which is certainly fair, and fits the analogy to algebra quite
well.

But in any case, parent's comment that "OOP has no special claim" to hidden
state/data (or abstraction) is sound; after all, ADTs already do that.

~~~
lmm
> Perhaps a better way of saying is is that hidden data is a characteristic of
> abstraction. Which is certainly fair, and fits the analogy to algebra quite
> well.

I'd disagree with even that much; I'd say abstraction is much more about
parameterisation - factoring out the common structure from different
constructions - than outright hiding anything.

> But in any case, parent's comment that "OOP has no special claim" to hidden
> state/data (or abstraction) is sound; after all, ADTs already do that.

ADTs don't hide _mutable_ state. That much is something OO has a special claim
to.

~~~
thrmsforbfast
"hiding data" and "factoring out the common structure" are almost always
intrinsically connected in software systems, because if you can poke at the
data then you can violate contracts that give rise to the common structure.

This is _particularly_ true of software, but I'd argue it's also true of
basically every branch of mathematics outside of a few particular sub-fields
of algebra (which are either extremely simple or extremely abstract, and often
both).

 _> ADTs don't hide mutable state. That much is something OO has a special
claim to._

How so? Abstract datatypes certainly hide mutable state. Just because a
language doesn't have the "private" keyword doesn't mean that internal data
can't be excluded from the interface... this was done _all the time_ in e.g. C
without the message passing semantics that characterize OO languages.

~~~
lmm
> if you can poke at the data then you can violate contracts that give rise to
> the common structure.

Maybe if you can mutate it, but having it be visible isn't an issue.

> Abstract datatypes certainly hide mutable state.

ADTs are normally understood to be values i.e. immutable. Hidden structure is
not the same thing as hidden state.

------
mlthoughts2018
The article lost credibility with me for the “OO is not about state” section
because it misses the point.

If all you have are singleton objects with static functions and no private
data attributes, then I agree, but it’s also meaningless because such objects
are very sincerely unrelated to anything from OOP. They are just stateless
buckets of functions, which is more like a module than an object.

This is actually quite a good way to program in many cases. You can do it in
Scala by only defining companion objects and never the actual class that it
would correspond to (except for occasional cases where you need case classes
or structural typing). You can do this in Python by just writing modules with
no classes, and either use primitive data structures (list, set, dict, tuple,
etc) for all your data, or else use absolutely minimal namedtuples.

I’d say this way of programming is almost totally FP and has hardly anything
but superficial connections to OOP, which I think undermines the author’s
point.

You can always claim it’s a semantic difference and different people mean
different things with certain words. IMO that’d be disingenuous here. Any type
of OOP that just uses objects as buckets of functions with no private data
attributes is really just FP.

As soon as you add private data attributes whose values can break referential
integrity of the object’s function calls, then it’s game over for FP and
you’re in total OOP land.

~~~
dnautics
> As soon as you add private data attributes whose values can break
> referential integrity of the object’s function calls function calls, then
> it’s game over for FP

Technically you can do something like this in fp land if you're using the
actor model to encapsulate hidden state and erroneous code in a passed lambda
does something strange to the hidden state. It's pretty hard to do since most
fps with actor model put all of your interfaces to the state right in front of
you.

~~~
chriswarbo
Actors are very closely related to OOP, especially the Smalltalk/Self message-
passing perspective. The main difference is that message sends to actors are
asynchronous whilst objects usually aren't.

------
thrmsforbfast
I think the author does characterize a primary distinguishing feature of OOP.

That said, the post is a windy road with some dead-ends. Lots of statements
that are highly contestable (see other top-level comments for examples).

So here is, IMO, the important take-away from the post on what's
novel/unique/significant about the combination of abstraction/program
design/programming techniques we call oop:

 _> In most software systems when one function calls another, the runtime
dependency and the source code dependency point in the same direction. The
calling module depends on the called module. However, when polymorphism is
injected between the two there is an inversion of the source code dependency.
The calling module still depends on the called module at run time. However,
the source code of the calling module does not depend upon the source code of
the called module. Rather both modules depend upon a polymorphic interface.
This inversion allows the called module to act like a plugin._

This is a particular programming style. The author calls it "polymorphism",
but that's far too large a label. The paragraph above is specific way of using
polymorphism. This sort of programming style _can_ be done in functional
languages, but I think it's fair to say that 1) OOP did it first, and 2) OOP
provides the most natural setting for thinking about what's going on in these
sort of "plugin architectures".

~~~
jpochtar
Can you help me understand the pullqoute? I didn’t understand what he’s
talking about at all. Is it about not having to recompile?

~~~
thrmsforbfast
See
[https://en.wikipedia.org/wiki/Inversion_of_control](https://en.wikipedia.org/wiki/Inversion_of_control)
and
[https://en.wikipedia.org/wiki/Dependency_inversion_principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle)

And don't overthink it. You've probably seen this a million times in your work
life. The description is indeed not great.

It's not always about not having to recompile, but can be (see eg dependency
injection)

------
08-15
There's a 2018 update/rehash here:

[https://blog.cleancoder.com/uncle-
bob/2018/04/13/FPvsOO.html](https://blog.cleancoder.com/uncle-
bob/2018/04/13/FPvsOO.html)

Not that it adds any value, it's exactly as confused and pointless. Uncle Bob
is getting old. He tries to find defining characteristics of OO, and ends up
talking gibberish:

> The behavior [of o.f()] is dependent upon the type of o. i.e. f is
> polymorphic.

Nope, behavior of a "polymorphic" invocation depends on the _value_ of the 'o'
argument. (Types don't exist at runtime.) This happens all the time in FP,
where passing around functions is normal.

> The mechanism of polymorphism must not create a source code dependency from
> the caller to the callee.

That's called a higher order function. You pass a function from module A to a
HOF from module B. B ends up calling into A without a source code dependency.
Big deal.

> What is FP? > referential transparency

Lispers would object. My take on it is that the essence of FP is higher order
functions. If you don't have them, you need Design Patterns instead. If you do
have HOFs, you don't see why anyone would bother with OO.

Bottom line: OO doesn't exist. It's just pointless jargon that only exists to
make a difference without a distinction (e.g. between Smalltalk and Scheme),
and possibly to sell books and Clean Code seminars.

------
Animats
Progress has come from making immutability more usable. Many of the troubles
in programs come from something mutating state that something else is
depending upon. This is not really a functional thing.

The important concept is "single assignment". This is where values are
initialized at creation and never changed. Go, which is an imperative
language, favors single assignment. Functional programming is a form of single
assignment.

Single assignment in an imperative syntax has some advantages over a pure
functional form. Values have names, which helps when reading code. You can use
the same value twice. It's functional programming without the cult.

The downside of single assignment is that you're frantically creating and
discarding values. Underneath, the machine is imperative. So you need compiler
support for efficient creation and discarding of values. LISP spends a lot of
time creating and discarding values, then garbage collecting them. So do
Javascript, Python, etc. Vast amounts of effort have gone into making that
efficient, and today it's not a big problem. Rust goes even further, handling
most of that overhead at compile time.

~~~
aikah
> Go, which is an imperative language, favors single assignment.

Go doesn't even have final variables. Go doesn't favor single assignment in
anyway. Rust does as it is harder to create a mutable variable than an
immutable one.

~~~
Animats
Go has "=" and ":=", to distinguish alteration of variables.

------
pka
> OO is not about state

OO is very much about state, and that's the fundamental difference between
non-FP languages and FP.

    
    
        a1 = object.f(b);
        a2 = object.f(b);
    

If a1 != a2 or f performs any side effects at all you've got state. Doesn't
matter if it's "private" or "hidden". Your functions are not referentially
transparent.

It also doesn't matter if your language implements OO in terms of message
passing (Smalltalk, Erlang) or method calling (Java, C#, ...).

~~~
millstone
This is orthogonal. Many languages traditionally considered functional do not
enforce referential transparency, and nothing prevents you from building
referentially transparent functions in an OO language.

~~~
addicted
Not speaking specifically about this situation, but just because a language
considered functional implements a certain behavior doesn’t mean the behavior
is functional. The causation usually works the other way. It’s because a
majority of the behaviors a language implements are functional that the
language is considered functional.

As an example, you can write procedural code in Java, which is the poster
child of OOP programming. That does not make the procedural code OOP.

------
TheCoelacanth
> Indeed, the word “variable” is a misnomer in a functional language because
> you cannot vary them.

Variables in functional languages are variables in the sense of mathematical
variables, i.e. they can take on multiple possible values. They don't need to
change from one minute to the next to be true variables.

------
mcphage
I do understand his initial frustration. Saying that FP languages solve the
Strategy Pattern (say) with functions is correct, but so reductionist as to be
useless. It's like saying you solved a problem using objects, or saying you
solved a problem using monads. Sure, you absolutely can, but _which_ monad you
use is actually _really important_. There are a lot of things that fall under
"The Strategy Pattern", and 'how you use functions to implement those' is just
as big (and interesting!) a question as 'how you use objects to implement
them'.

~~~
08-15
The Strategy Pattern comprises a single interface with a single method. In
other words, the Strategy _is_ a function, so it's entirely appropriate to say
the FP equivalent of the Strategy Pattern is "function". Objects and
interfaces just obscure that simple essence.

~~~
mcphage
See, that’s what I mean about being reductionist. Is your answer wrong? Eh,
not entirely, it’s just useless. Plenty of uses for the Strategy Pattern
require more than just a function, and even those that a function is
sufficient, what the function returns, and when, and why, matters. Reducing it
down to a single function offers no understanding, it just sidesteps the
point.

------
bcheung
I normally like what "Uncle Bob" has to say but I do not agree with a lot of
what he says in the article.

> Objects are bags of functions, not bags of data.

That might be an ideal case, but it is not reality. Pretty much every OO
language has both of them combined.

> Is there really so much difference between f(o), o.f(), and (f o)? Are we
> really saying that the difference is just about the syntax of a function
> call?

Yes, there is.

Try doing a compose function with the OOP / member expression syntax.

Here it is in JS using FP syntax:

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))

It would be even shorter in Haskell.

Try doing long division with Roman numerals.

Syntax is the structure behind mental representations and mental
representations have tremendous power in expanding the physical limits of
thought in the brain.

> The overriding difference between a functional language and a non-functional
> language is that functional languages don’t have assignment statements.

The overriding difference between FP and OOP languages is that the unit of
composition and abstraction is the function or object, respectively.

------
kazinator
> _Objects are bags of functions, not bags of data._

That is just silly. I'd be willing to swallow the following revised version,
in the context of immutable programming: " _classes_ are bags of functions,
not bags of data."

An object (class instance) is then just _a_ datum which serves as a domain
value to these functions. Given that domain value, each function in that bag-
of-functions class produces a range value: and that bag of range values
comprises the properties of the object.

Under traditional mutable OOP, those functions are impure in ways that
logically require the object-datum to carry a representation of state somehow.

And anyway, since functions are data (being first class values), any bag of
functions is necessarily a kind of bag of data.

------
jordache
I think the point of the two styles not being mutually exclusive is helpful.
So often, is the literature treating the two concept like either-or, very
black and white....

------
Avi-D-coder
The statement author makes that polymorphism is belongs to OPP is incorrect.

Functional languages by design require better type systems that can
accommodate more forms parametric polymorphism.

OPP traditionally encouraged the use of ad-hoc polymorphism (via classes) over
parametric polymorphism (via generics). OPP languages generally also make it
easy to perform Ad-hoc polymorphism at run-time with dynamic dispatch some
languages even use dynamic dispatch by default.

TLDR: FP and OPP both have polymorphism. FP encourages parametric
polymorphism. OPP encourages ad-hoc polymorphism.

~~~
joel_ms
Ad-hoc polymorphism is interfaces in java, typeclasses in haskell, implicits
in scala etc. Classes (usually) enable _subtype polymorphism_.

~~~
Avi-D-coder
I would argue that subtype polymorphism is functionally equivalent to Ad-hoc
polymorphism especially in the presence of multiple inheritance.

The distinction is how polymorphic code is written not what the semantics can
be expressed. E.g. rust traits allow for dynamic dispatch. While there is a
distinction between ad-hoc polymorphism and subtype polymorphism, I don't
believe it is necessary when comparing what semantics FP and OPP languages
express.

------
d--b
please add (2014)

~~~
ruairidhwm
Done!

~~~
d--b
thx ;)

