
Why OO Sucks by Joe Armstrong (2000) - andrewl
http://www.cs.otago.ac.nz/staffpriv/ok/Joe-Hates-OO.htm
======
rhblake
It really should be noted that years later Joe changed his mind about OO and
came to the realization that perhaps Erlang is the _only_ object-oriented
language :) From a 2010 interview:

..."I wrote a an article, a blog thing, years ago - Why object oriented
programming is silly. I mainly wanted to provoke people with it. They had a
quite interesting response to that and I managed to annoy a lot of people,
which was part of the intention actually. I started wondering about what
object oriented programming was and I thought Erlang wasn't object oriented,
it was a functional programming language.

Then, my thesis supervisor said "But you're wrong, Erlang is extremely object
oriented". He said object oriented languages aren't object oriented. I might
think, though I'm not quite sure if I believe this or not, but Erlang might be
the only object oriented language because the 3 tenets of object oriented
programming are that it's based on message passing, that you have isolation
between objects and have polymorphism.

Alan Kay himself wrote this famous thing and said "The notion of object
oriented programming is completely misunderstood. It's not about objects and
classes, it's all about messages". He wrote that and he said that the initial
reaction to object oriented programming was to overemphasize the classes and
methods and under emphasize the messages and if we talk much more about
messages then it would be a lot nicer. The original Smalltalk was always
talking about objects and you sent messages to them and they responded by
sending messages back."

See [https://www.infoq.com/interviews/johnson-armstrong-
oop](https://www.infoq.com/interviews/johnson-armstrong-oop) (2010) for the
full answer (and more), it's worth a read.

~~~
tux1968
Isn't a method call a message, and the return value a message back? Or is it
that "true OO" must be asynchronous?

~~~
revvx
> Isn't a method call a message, and the return value a message back?

It is!

In my view, the point that Alan Kay and Joe Armstrong are trying to make is
that languages like C++/Java/C# etc have very limited message passing
abilities.

Alan Kay uses the term "late binding". In Kay's opinion, "extreme late
binding" is one of the most important aspects of his OOP [1], even more
important than polymorphism. Extreme late binding basically means letting the
object decide what it's gonna do with a message.

This is what languages like Objective-C and Ruby do: deciding what to do after
a method is dispatched always happen during runtime. You can send a message
that does not exist and have the class answer to it (method_missing in Ruby);
you can send a message to an invalid object and it will respond with nil
(Objective-C, IIRC); you can delegate everything but some messages to a third
object; you can even send a message to a class running in other computer
(CORBA, DCOM).

In C++, for example, the only kind of late binding that you have is abstract
classes and vtables.

-

> Or is it that "true OO" must be asynchronous?

It doesn't have to be asynchronous, but in Alan Kay's world, the asynchronous
part of messaging part should be handled by that "dispatcher", rather than
putting extra code in the sender or the receiver.

I don't remember Alan Kay elaborating on it, but he discusses a bit about this
"interstitial" part of OOP systems in [2]

-

[1] -
[https://en.wikipedia.org/wiki/Late_binding](https://en.wikipedia.org/wiki/Late_binding)

[2] -
[http://wiki.c2.com/?AlanKayOnMessaging](http://wiki.c2.com/?AlanKayOnMessaging)

~~~
bad_user
C++'s vtable is also late binding, since you don't know which implementation
you're calling until runtime. And there's no such thing as "extremely late
binding".

> _In C++, for example, the only kind of late binding that you have is
> abstract classes and vtables._

That's not true, you can always have a "send_message(string id)". Few people
do it because you lose static type safety. And some languages, like C# and
Scala, have dynamic types that allows for the "method_missing" protocol and
such features are very unpopular.

To be honest I don't see much of a difference. I've worked with a lot of
dynamic OOP languages, including with Erlang-style actors and I've never seen
the enlightenment of dynamic OOP message passing.

And I actually like OOP, but I don't really see the point of all this
hyperbole about Smalltalk.

~~~
foobar_
> That's not true, you can always have a "send_message(string id)". Few people
> do it because you lose static type safety. And some languages, like C# and
> Scala, have dynamic types that allows for the "method_missing" protocol and
> such features are very unpopular.

That is the difference. If every class in C++ had only one method -
send_message and each object is an independent thread, you will get how Erlang
works. That is how you would do the actor model in C++.

Inheritance, Polymorphism is emphasised in Java, C++ and C#, whereas
Functional programmers emphasise function objects / lambdas / Command Pattern
where you just have one method - calling the function. Infact having just
method you no longer need Polymorphism / Interfaces.

~~~
bad_user
What? This has nothing to do with functional programming.

FP needs polymorphism too and as a matter of fact FP tends to be even more
static.

In FP we have type classes, built via OOP in static OOP languages.

> _Infact having just method you no longer need Polymorphism / Interfaces._

That’s false.

~~~
foobar_
It's not. You can use multiple dispatch.

~~~
sideshowb
But then why would you? Isn't ditching type safety a bad idea most of the time
?

------
vhb
Well, I disagree with 99% of this... I'm a guy that started with C, moved to
functional programing, added C++, and now do all 3.

> Objection 1. Data structure and functions should not be bound together

Well, in my experience, in every almost every code-base (either from
functional, or imperative programing), we end up with modules, witch are a set
of function taking the same type as a parameter. This is very close to binding
the functions and the types...

> Objection 2. Everything has to be an object.

I don't get the example. The only thing that this show, is the benefits of
having a range type built in the language. Then it's just type aliases.

"There are no associated methods.", yes, but you will need functions to
manipulate those types (just translate one type into another), at the end,
it's going to a module, which is almost an object.

> Objection 3. In an OOPL data type definitions are spread out all over the
> place.

That's true. It also makes thinking about the data layout complex. That's why
other paradigm have been developed (DOP), on top of OOP. Now you can also
think that having those defined together makes dependency management easier.

> Objection 4. Objects have private state.

False. Objects _can_ have a private state. This a problem with mutability, not
oriented object programing. You can have non mutable OOP.

> Why was OO popular?

>> Reason 1. It was thought to be easy to learn.

The past 20 years have shown how easy it is. In fact, I actually think it's
too easy, people rely too much on abstraction, without even trying to
understand what's going on. I my opinion, it promotes a lazy mindset (This is
my biggest criticism about OOP).

>> Reason 2. It was thought to make code reuse easier.

I would like an evidence that it's not.

>> Reason 3. It was hyped.

True, but that does not make it bad. People tried to hype every
technologies... Some stayed, some went away.

>> Reason 4. It created a new software industry.

How has OOP created a software industry that would not have existed if
functional programing had "won the fight"?

~~~
endgame
Upvoted because it's well-articulated, even though I disagree.

> Well, in my experience, in every almost every code-base (either from
> functional, or imperative programing), we end up with modules, witch are a
> set of function taking the same type as a parameter. This is very close to
> binding the functions and the types...

There is a key distinction: If I have two subsystems that use the same data in
different ways, I can keep those concerns separate by putting the functions
for each concern into a different module. Binding all the functions to the
type mixes the concerns together and creates objects with way too much surface
area.

Also, most OO langs make a big ceremony out of each new type: create the class
file, create the test file, blah blah blah. I want types to be cheap so I can
make them easily and capture more meaning with less work.

~~~
vhb
> Upvoted because it's well-articulated, even though I disagree.

Appreciate it :)

> There is a key distinction: If I have two subsystems that use the same data
> in different ways, I can keep those concerns separate by putting the
> functions for each concern into a different module. Binding all the
> functions to the type mixes the concerns together and creates objects with
> way too much surface area.

This is where composition helps. Now, historically, indeed OOP programmers
have not been the best at using composition. Now, looking at more recent
projects, this has got a lot better.

> Also, most OO langs make a big ceremony out of each new type: create the
> class file, create the test file, blah blah blah. I want types to be cheap
> so I can make them easily and capture more meaning with less work.

Totally agree with that, the ability to define a type in one line and have it
reflected though the entire code base through type inference is the one thing
that I miss the most in C/C++.

~~~
endgame
> This is where composition helps.

It does, though in my experience it leads you down a path that ends in some
pretty strange names, as you nominalise more and more nebulous concepts,
trying to verb in the kingdom of nouns.

~~~
peeters
Is that any different from foldl, foldr, reduce, map? If you have a generic
data type you want your operators to be generic, regardless of whether they
exist as methods or as separate functions. The only difference is that the
object is free to not leak internal implementation details.

------
keithnz
Everything sucks, they just all suck differently.

I still think OO provides a pretty easy mental framework for programming. You
can get good results. Bit of discipline without going crazy and it works
really effectively. Despite its shortcomings.

~~~
gboudrias
Agreed. I remember thinking "what don't I get? Why do we need getters and
setters?". After some years (and discovering Python), I realized there's
nothing to get, it's just ridiculous overengineering 95% of the time. Same
goes for a lot of stuff in OO. I attribute it to the corporate mindset it
seems to thrive in, but I could be wrong.

~~~
kbp
The important thing is restricting your public interface, hiding
implementation details, and thinking about how easy your code (and code that
uses it) will be to change later. It's not an OO vs anything thing.

When you want a value from a module/object/function/whatever, whether or not
it's fetched from a location in memory is an implementation detail. Java and
co provide a short syntax for exposing that implementation detail. Python
doesn't: o.x does not necessarily mean accessing an x slot, and you aren't
locking yourself into any implementation by exposing that interface as the way
to get that value. It's more complicated than Java or whatever, here, but it
hides that complexity behind a nice syntax that encourages you to do the right
thing.

Some languages provide short syntax for something you shouldn't do and make
you write things by hand that could be easily generated in the common case.
Reducing coupling is still a good idea.

~~~
weberc2
I don’t think I’ve ever seen a useful “Getter” abstraction...

~~~
Misdicorl
It's frustrating to read this thread and your comment kind of crystallized
this for me so I'll respond to you.

Using an array without having to (manually) calculate the size of the objects
contained within is like the major triumph of OO. This is a getter that you
almost certainly use constantly.

Please try to consider your statements and potential counter factuals before
spraying nonsense into the void

~~~
weberc2
> Please try to consider your statements and potential counter factuals before
> spraying nonsense into the void

My claim was that getter abstractions as described by the GP (abstracting over
the “accessed from memory” implementation detail) are not useful. Why do you
imagine that your array length example is a reasonable rebuttal?

~~~
Misdicorl
Its not the length of the array. Its using things like array[20]. Yes that
exists pre-OO and outside of OO, but its the foundational aspect of OO and one
of the strongest use cases.

Sorry for the way I communicated- I was tired and should have reconsidered.

~~~
weberc2
> Sorry for the way I communicated- I was tired and should have reconsidered.

No worries, it happens. :)

> Its not the length of the array. Its using things like array[20]. Yes that
> exists pre-OO and outside of OO, but its the foundational aspect of OO and
> one of the strongest use cases.

I'm not sure what you're getting at then. Indexing into an array? Are you
making a more general point than arrays? I'm not following at all, I'm afraid.

~~~
Misdicorl
I think my argument is basically that arrays are effectively object oriented
abstractions in most languages.

You aren't responsible for maintaining any of the internal details, it just
works like you want it to. My example was with the getter for the item at
index 21 (since you had specifically called out useless getters), but equally
well applies to inserting, deleting, capacity changes, etc.

~~~
weberc2
> I think my argument is basically that arrays are effectively object oriented
> abstractions in most languages.

I think I see what you mean, although I think it's worth being precise here--
arrays can be operated on via functions/methods. This isn't special to OO; you
can do the same in C (the reason it's tedious in C is that it lacks generics,
not because it lacks some OO feature) or Go or Rust or lisp.

These functions aren't even abstractions, but rather they're concrete
implementations; however, they can implement abstractions as evidenced by
Java's `ArrayList<T> implements List<T>`.

And to the extent that an abstract container item access is a "getter", you're
right that it's a useful abstraction; however, I don't think that's what most
people think of when they think of "getter" and it falls outside the intended
scope of my original claim.

------
peterashford
I'm really sick of these 'why blah sucks' posts. Clearly OOP works for a lot
of people. If it doesn't work for you, don't use it. My personal feeling is
that FP works better when the problem domain is more data oriented, requiring
transformation of data streams whereas OOP is good when the problem domain is
about simulating or modeling where you want to think about interacting agents
of some kind. The whole 'X is one true way' argument is narrow sighted. I feel
the problem should always precede the solution

~~~
chx
> If it doesn't work for you, don't use it.

How many of us actually get a choice in this...?

~~~
mikekchar
I wonder if it would be constructive to modify the OP's statement a little
bit. You can get there from here using any approach (as long as it is Turing
complete ;-) ). Some approaches will work better than others, but optimising
your approach first and convincing others second is putting the cart before
the horse. Having a happy team that works well together is going to provide at
least an order of magnitude more ROI than choosing the best approach.
Compromising on your approach to make others happy will almost certainly pay
off hugely. Get good at as many types of approaches as you can so that you can
take advantage of those payoffs. The cult of "I must use the absolute best
approach for this problem, everyone else be damned" is one that leads to
misery IMHO (especially if it turns out that your "best approach" isn't, which
happens most of the time in my experience ;-) ).

------
S_A_P
I agree with a lot of the arguments made in this post. However I think there
is an assumption that programming is programming and there aren’t different
problem sets to solve. To me, UI development lends itself naturally to the
everything is an object paradigm. I need a button to start event x. Hmm the
button needs to know what size it is and other miscellaneous attributes about
itself. Oh it also should have a function that handles what to do when the
button is pressed. Hey that sounds a lot like an object that has state, data
structure and functions all in one. Now as we traverse the environment to
basic CRUD type applications, OOP can be overkill. If all you want to do is
pull x number of rows out of a database and put them in a dataset, do we
_really_ need objects for this? Nope. Most of the time the objects for these
types of problems are just wrappers around an array of pointers. Creating DAO
type objects is a lot of the time more trouble than it’s worth, but if were
already in an OO paradigm, why switch?

------
Const-me
> The “hide the state from the programmer” option chosen by OOPLs is the worst
> possible choice

When you code C and write something as simple as

    
    
        fwrite( "Hello world", 1, 11, stream );
    

that line of code changes state of the file stream object in CRT, state of
file caches in OS, state of B-tree nodes in file system driver, state of disk
firmware, state of NAND flash chips…

It’s not just OS and drivers. Any sufficiently complex software is built by
layering abstractions on top of each other.

Many problems that need to be solved have very complex state, often related to
IO or GUI. You don’t have other choice but to use some form of OOP and hide
lower-level implementation details behind abstractions, otherwise you won’t be
able to get anything done due to overwhelming amount of state to reason about.

~~~
andy_ppp
Nobody is saying don’t have _any_ abstractions, just that your program hiding
state in layers of objects is often counterproductive.

~~~
Const-me
> program hiding state in layers of objects is often counterproductive

Do you think it was counterproductive to hide OS-specific file handles, CRT
level of caching, and many other things CRT does behind these opaque FILE*
object pointers?

Such hiding allowed to have very similar and quite easy to use <stdio.h>
across all platforms.

The implementation of these fopen and fwrite functions relies on kernel calls
like open/write on Linux and CreateFile/WriteFile on Windows. Do you think it
was counterproductive to hide all the lower-level stuff like OS caches, inodes
(linux) / NTFS (windows), and the rest of them, behind these OS kernel calls?

If you’ll decide to unhide them, your OS won’t have any security at all, the
OS file system cache is security sensitive. Modern OSes implement strict
access control while they hide that piece of state, every file has
permissions, every process accessing them has user identity, and the OS checks
these things when process access files or file system cache.

~~~
jstimpfle
> Do you think it was counterproductive to hide OS-specific file handles, CRT
> level of caching, and many other things CRT does behind these opaque FILE*
> object pointers?

To answer that question for parent, no, he obviously doesn't think so. And
yes, streams are basically OOP (they are implemented using "method dispatch").
Streams are one of the few successful abstractions out there.

And when I say "successful" I mostly mean "necessary". Because they're leaky.
Consider fseek(3), ftell(3), fileno(3), fflush(3), fclose(3), fsync(2),
posix_fadvise(2), isatty(2), fcntl(2), ioctl(2) and many more. All operations
that are not supported for all streams, but _necessary_ for some (like file
system files, network connections, terminals devices...). Essentially when you
use any of these you concede to break the abstraction.

It's _incredibly_ hard to handle these streams correctly, and the vast
majority of programs are faulty in that they don't handle most error
conditions correctly. In some cases, it isn't even possible to handle errors
through the API! See close(2)

So again, not saying we shouldn't have a stream abstraction. But abstractions
come at a huge price, and therefore it's incredibly misguided to make every
program a layering of thousands of pseudo-isolated objects that bring their
own bug-prone abstractions and idiosyncrasies.

~~~
Const-me
> Streams are one of the few successful abstractions out there.

I don’t think they’re few. I think all modern software uses OOP-based
abstractions heavily.

For example, for GUIs, it’s visual trees everywhere. HTML DOM is the most
widely used one now, but pretty much every GUI framework have something
conceptually similar, even 30 years old WinAPI is built on very OOP-like
concepts, you have HWND handles but the state is completely hidden and can
only be accessed by calling functions.

> Because they're leaky.

No abstraction is 100% waterproof, but some are useful enough for many
practical applications, despite they leak occasionally.

These particular file I/O API is less leaky on Windows, but they still leak,
and there’re many weird low-level APIs to bypass these abstractions:
DeviceIoControl, defragmentation API, backup API, and many others.

> abstractions come at a huge price

I’m not convinced the price is > 0.

We can always skip any abstraction we don’t like and use the next lower-level
one. We don’t often do that because developing code based on these lower-level
things is often more expensive than using the abstractions, despite they leak.

That’s why people use Electron & JavaScript, Java & .NET, game engines. They
all made from large count of the layers you’re talking about, and when they
leak that’s indeed can be very expensive to diagnose and fix/workaround.
However, dropping them and developing something from lower levels is usually
way more expensive.

~~~
jstimpfle
The last Electron app I've used (one of these new chat apps, forgot the name)
was dog slow (on my $700 computer from 2018), and gave my computer a hang up
(probably triggered a graphics driver issue, or sth related with too much
memory consumption).

~~~
Const-me
I don’t like Electron too much, and I don’t program Electron apps (I mostly
code C++ and C#), but I saw good products built on Electron. For example,
visual studio code is fine.

It’s the same story with all high-level tools. They’re easier to use and have
lower barrier to entry, used by less skilled programmers who fail to deliver
good products.

For example, Unity3D has very low barrier and there’re many low-quality games
built with that. But if developers know what they’re doing it is possible to
build great games, e.g. cities skyline.

------
Animats
A few notes:

Binding data and functions together beats operating on global data visible to
everything. One of the big wins of OOP is less exposed global data.

A big problem with OOP in C++ was that it wasn't opaque enough. The headers
needed to use an object contain info only useful to the object's private
functions. This creates recompile hell.

Multiple inheritance is seldom worth the headaches.

Overriding a member function needs declaration support. The usual cases are 1)
the parent function needs to be called first, 2) the parent function needs to
be called last, and 3) the parent function needs to not be called at all.
Which case is to be used is properly a property of the parent, but in most OOP
languages, it's expressed as code in the child.

The use cases for generics are rather limited. Collections, yes. Beyond that,
it's usually someone getting cute. That way leads to the "metaprogramming"
mess. Go has a few built in parameterized types - maps, arrays, and channels.
Go2 may have generics, but the designers are struggling with what to add that
doesn't take them down the rabbit hole.

Objects are probably more useful than some of the things invented to replace
objects, like "traits".

~~~
weberc2
> Binding data and functions together beats operating on global data visible
> to everything. One of the big wins of OOP is less exposed global data.

“Passing state as parameters” is what solved the “everything operating on
global state” problem. Binding functions and state permitted
polymorphism/abstraction.

> Multiple inheritance is seldom worth the headaches.

 _Single_ inheritance is never worth the headaches. :)

> Objects are probably more useful than some of the things invented to replace
> objects, like "traits".

Traits don’t replace objects; they are interfaces with static dispatch
semantics.

~~~
humanrebar
There are so many kind of polymorphism. Many of them do not require objects.
Executing a closure is a form of polymorphism. As is manipulating a generic
type.

~~~
weberc2
Agreed, but objects are the implementation used in OO. (My point was not that
they are the only implementation).

------
mises
I think this may be another one of those swearing-in-church type opinions, but
I think C++ is excellent as a compromise here. It's got its faults, but C++
allows use of objects when necessary or helpful without the ridiculously
pedantic OO of Java. It is quite nice to be able to use it when convenient
while not having to do so when stupid.

~~~
QuackingJimbo
You getting downvoted for this completely inoffensive (at worst) comment is
pretty sad

I swear C++ gets a bad rap because most people have only seen a gross mixture
of C++98 and C in their codebases

A fully modern C++11 codebase is a beautiful thing

~~~
bhaak
Why are you so sure the downvotes are not coming from the opinion of the
"ridiculously pedantic OO of Java"?

~~~
mises
Maybe because no body had commented as such until now? I am rather practical
about this: I don't really like Java because it forces things that make more
sense as functions to be under an object. What if I just want a simple global
utility function, and don't want to make every single class inherit from a
class containing only that? It's a lot of unnecessary hassle for something
like that. I'm not saying OO is bad, I said it was too pedantic. I like
languages which allow you to do what makes sense.

~~~
pvg
_and don 't want to make every single class inherit from a class containing
only that_

That's not how having a global utility function works in any OO language I can
think of.

~~~
mises
You could also make a class of global functions and use that, but I think the
point still stands: it forces square pegs into round holes, so to speak.

~~~
fnrslvr
> You could also make a class of global functions and use that

I'm not sure I understand your issue with doing this. You need to put your
global (i.e. public static) functions in classes not because Java is forcing
OO practices into everything, but because classes effectively serve as Java's
translation units. I think they serve this purpose pretty well in practice.

~~~
mises
Classes being Java's _only_ translation units are one of the downsides that
I'm mentioning. Really my objection is more that it doesn't allow use of the
right tool for the job (which is not always an object).

~~~
fnrslvr
I'm not sure I see where objects enter into this. I mean, you seem to be
asking for something like this,

    
    
      public unit Util {
          public void foo() {
              // do things
          }
      }
    

which would compile down into a bytecode-containing artifact which we could
call a unit file, which the JVM would load at runtime using some sort of unit
loader. Callers could import the Util unit and then invoke foo with the
statement

    
    
      Util.foo();
    

But then, I don't see the diffence between the above and the following,

    
    
      public class Util {
          public static void foo() {
              // do things
          }
          // If we're really pedantic, we can ban construction of Util instances
          private Util() {}
      }
    

which compiles down into a bytecode-containing artifact called a class file,
which the JVM loads at runtime using a class loader. Callers can import the
Util class and then invoke foo with the statement

    
    
      Util.foo();
    

What's this downside you speak of? Is there something a different kind of
translation unit would do that a class currently doesn't?

------
dang
This doesn't contain his most famous line on the topic:

 _You wanted a banana but what you got was a gorilla holding the banana and
the entire jungle._

[https://www.johndcook.com/blog/2011/07/19/you-wanted-
banana/](https://www.johndcook.com/blog/2011/07/19/you-wanted-banana/)

~~~
freddie_mercury
Why would a post written before 2000 include a quote from an interview done in
2008?

~~~
dang
It wouldn't!

------
kbumsik
I have seen a lot of posts bashing OOP by FP advocates. They write so many
points explaining why OOP sucks, however, strangely few of them try to present
why and how FP can be better in those points. Most of them assume that FP is
unconditionally better in all aspects of coding, so they don't even try to
explain.

This post is not different.

~~~
zapzupnz
They also falsely assume that bad OOP practices in the industry == OOP as a
whole, full stop. Arguments against OOP tend to be strawmen arguments.

I mean, sure, Java taught bad OOP to millions of developers in the 90s, early
00s, and even today. Sure, the legacy of bad OOP lives on today.

That doesn't say anything about the principles of OOP when applied properly,
when done in languages that avoid the pitfalls of bad OOP, and when the
developer avoids the very patterns that tend to fall into criticism.

(incoming FP advocate to call my statement a strawman argument in 3, 2, 1…)

------
jasonhansel
Worth noting that most of these criticisms also apply to existential types,
which are fairly common in functional languages (e.g. Haskell).

~~~
l_dopa
That's not entirely accurate. Abstract data types (e.g. ML modules) can also
be modeled as existential types and don't have the same problems.

I think it's the particular combination of only having nominal typing, only
allowing the oo-flavor of data abstraction, and encouraging programming with
state and using inheritance for code reuse that leads to the object-spaghetti
nonsense in heavily-oo Java/C++ codebases.

~~~
jasonhansel
I just mean that (at least in Haskell) existential types provide information
hiding & combine functions with data. In fact, they're implemented with
dynamic dispatch, just as with OOP.

The problem is not that OO languages have these features; the problem is that
they _lack_ other features common to functional languages.

------
billconan
> Objection 1. Data structure and functions should not be bound together

I don't understand the above:

a. functions do things,

b. objects don't do things,

therefore, we shouldn't combine them together?

Why? I'm not convinced? what's the logic? doesn't seem to be a strong argument
to me.

factories make things.

warehouses don't make things.

so we should build them together?

~~~
crubier
We should _be forced_ to combine them together.

------
wmnwmn
I've felt that way for years. OO perhaps makes sense for certain things,
mainly things that someone else writes and you use as a library. Using OO more
widely leads to very inflexible code, and since specs are not inflexible, the
result is disaster.

------
jrochkind1
> In an OOPL I have to choose some base object in which I will define the
> ubiquitous data structure. All other objects that want to use this data
> structure must inherit this object.

So... that's not actually how it works though, right?

~~~
brians
It was. Criticisms like this led to implementing hybrid-OO systems. Those are
what we now call OO. He’s talking about Smalltalk, Self, that sort of thing.

~~~
jrochkind1
In no ever existing OO language do you have to inherit from a data structure
to use the data structure, I don't think?

------
Havoc
>Consider “time”. In an OO language a “time” has to be an object.

No it fukin doesn't. OO is a paradigm, useful at times, nothing more.

That's what modern programming is all about - you pick the right tool for the
right task.

OP's attempt to force OO onto an abstract concept like time is so far off the
mark calling it a strawman wouldn't even be the right word.

------
ilovecaching
Objects should be used when you need to maintain invariants between members,
or need an opaque stateful "thing" like a resource handle. They shouldn't be
used to structure a program, but act like any other type in a functional or
procedural setting.

An object should also have completely private members and no concept of
inheritance. This greatly simplifies thinking about and designing objects.
There should be no distinction between a regular and special method like a
constructor or destructor. They should essentially be simple constructs with
no magic that force interacts with a particular kind of data through functions
that maintain the invariants. That's it. That aligns with the message passing
origin of OO and Erlang in that there is no "breaking the rules" to get to an
objects state.

This is basically how they work in Rust and Go. The key in both of these
languages is the use of traits and interfaces to obtain special behavior and
polymorphism. Neither of them forces a composite type to be completely public
or private though, and Go limits visibility to the package level.

I used to really hate OO, but I started to think about what the parts I really
disliked about it, and saw that in Erlang, Haskell, etc there is always a need
for an "object" whether it be a GenServer or a Monad. Java and C++ just took
the concept too far, and took an absolutist approach that most features need
to be part of the OO machinery, although Java is by far the worst. Objects
have their place, but they should be just as common as any of other type, and
should not be used to think of program structure in most cases.

------
graycat
I don't mind _object oriented_ programming too much: Occasionally, at most a
small fraction of the time, it helps me write good code, and then I use it.
Otherwise I don't.

Here are some of the things I'd like to see in programming languages:

(1) Some semantics that admit some useful static analysis, that is, tell me
some useful things about my code, e.g., for a variable, where does it get used
and where might it get changed?

(2) Offer me some useful _code transformation_ properties. For this, a start
is the relatively powerful _scope of names_ constructs in PL/I: There can just
drop into the source code another function and know that in the more common
and important respects that function being there won't hurt anything else
already there. So, if I have some function I like for something or other, then
I can just drop it in.

There's a lot more: The main point is just to try, so that given 100,000 lines
of typing of code, some tools can tell me where the lines of spaghetti and the
meatballs are!!!!

So, that code is a _system_ and treat it as such, i.e., instrument the thing
and tell me what is going on, both _statically_ before it runs and as it runs.

------
jussij
I have worked on systems that had turned into the classic 'big ball of OOP
spaghetti'.

Likewise I've also had to deal with a 'big ball of structured programming
spaghetti'.

My take on the difference in these two types of systems is they are generally
the result of too much coding with too little design.

Personally, I think the OOP approach does have a tendency to turn into that
spaghetti ball more easily, only because it needs more up front design.

Most of places I've worked who adopted an OO approach have tended to focus
predominantly on the OOP with little or no attention to the OOD and that tends
to be a recipe for disaster.

------
namelosw
The biggest problem of OO languages for me is - it always depends.

Composition or inheritance or template or concrete? It depends. Extract
methods? It depends. Design patterns? It depends.

Although to master good OO design - knowing what and when to do something - is
surely an art, it doesn't handle dynamic business model very well.

If I just express the business, I cannot handle requirement change. If I want
to deal with some future change, I have to do a lot of non-business oriented
design, making the code less expressive.

And most of the OO language don't have algebra data types, which makes
expressing business model more awkward.

------
lsiebert
Object oriented programming isn't about an individual programmer.

It's about creating discrete APIs so that a bunch of different programmers can
work on different aspects or sections of code.

It's an organizing principle for discrete elements that encapsulates internal
state. You don't need to know your datastore is SQL or Redis, or Redis caching
SQL data, or marshalled JSON, you just call Users.getUser(id), and get a User
Object with a defined API.

OO is in many ways just microservices for a single codebase.

I think people ignore the importance of OO in terms of unit and integration
testing.

~~~
weberc2
You just described abstraction. Abstraction is not specific to OO...

The only thing that strikes me as specific to OO is implementation
inheritance, and that has been an unmitigated disaster.

------
kazinator
IF you don't like data and functions bound together, then use a CLOS-like
object system with generic functions that don't belong to classes, but have
methods whose arguments are merely specialized by class types.

Functions and data are in fact tied together. Almost any thing in mathematics
is described in terms of some collection of properties which are elements of
sets, and operations. The operations are part of the representation.

A Universal Turing machine is the tape, the symbols, the rules of how the head
moves and reads and writes symbols, all together.

------
rkagerer
My lukewarm enthusiasm for OO stems from a different set of reasons than
these. I view its main benefit to be the way it reinforces encapsulation as a
design pattern. But good code with clearly organized functions and data
structures can achieve that just as well.

I don't have anything against it, and wielded with skill I see its benefit.
But after 30 years of programming I think you can achieve many of those
benefits more simply without it.

------
ganonm
I think this article is quite old, so I can forgive the author for being out
of touch with modern OO language practices. Nonetheless, I find myself
disagreeing with almost all of his arguments.

When he talks about state for example, I assume he means _mutable_ state.
Everyone knows this is best avoided if possible. The vast majority of OO
languages provide mechanisms to avoid mutability e.g. data classes or keywords
to make references constant.

He also rails against private state specifically. I assume again that he means
private mutable state. This is generally a bad idea and is accepted pretty
uncontroversially as a bad idea. The principle of data hiding in general
however is definitely _not_ a bad idea. Being able to enforce scoping rules on
classes/functions/members is not meant to facilitate creating 'black box'
entities whose internal mutations are difficult to reason about, but is
instead meant to provide guarantees about dependencies between different parts
of a system, i.e. Separation of Concerns.

Regarding 'everything is an object', in many cases this is perfectly
acceptable as it guarantees some sort of basic interface that all entities
conform to e.g. being able to ask an entity to provide a hash code, or to
carry out a comparison check with some other entity. Some modern languages
like Kotlin even eschew the concept of primitives altogether and just make
everything an object.

Finally, regarding his point about OO languages having data type definitions
all over the place, I must be misunderstanding something here... Why on Earth
would you want all your data type definitions in one place?

Anyway, just wanted to give my $0.02 lest anyone get the impression that these
are 'knockout punches' against any and all OO languages.

~~~
macintux
> Everyone knows this is best avoided if possible. The vast majority of OO
> languages provide mechanisms to avoid mutability e.g. data classes or
> keywords to make references constant.

Well, clearly not everyone knows it.

My biggest complaint about languages that incorporate functional features is
that immutability is most useful when it’s the 99% use case.

When it’s a special tag that may or may not be used within the library/code
base you’re using, you don’t get the cognitive advantage of feeling confident
you can judge the output of a function by reasoning about its input.

My brain is small. I need all the crutches I can get, and referential
transparency is a huge advantage.

~~~
ganonm
Yes, I agree, I don't understand why modern languages don't make references
immutable by default. The best I've seen is symmetry between val and var.
Talking about safe defaults, I remember being very disappointed when Jetbrains
changed the default scope in Kotlin to be public....

------
stonefarfalle
Objection 1: Given a queue, priority queue, and stack, how do those data
structures "just exist", without the behavior associated with them? The
interactaction with them is their defining characteristic.

Objection 2: I agree not everything should be an object. However where do you
draw the line? To me a timestamp is a perfectly reasonable object. I can see
the pros and cons of "3" being an object as well.

Objection 3: I like things to be organized. What benfit do I get by jumbling
everything together in one spot?

Objection 4: Yes they do, pretending they don't doesn't help. Even in Haskell,
files and sockets are represented as an opaque handle to hidden state. It very
much looks like a OO interface in a non OO language. Given the three data
structures in objection 1, hiding the state allows for a smaller surface area
to prove that the data structures are correctly implemented. Allowing random
access to their state does not improve them.

------
axilmar
I disagree with every point he made.

Data structures shall be bound to functions. It increases modularity. That
does not mean common code shall not be refactored and be standalone.

Data types are instantiated to objects. When in Erlang a time variable is
declared, that variable is an object. OOP has data types too, called classes,
and having everything to be an object does not mean it has to be the same data
type.

In some languages classes can be in the same file, so point #3 is actually
about specific implementations.

Inside point #3 he also mentions inheritance. Not all OOP languages require
inheritance from a specific class.

I also disagree with his 4th point about hidden state: hidden state enhances
modularization tremendously. I don't need to see how something is implemented,
treating it as a black box increases my chances of using it correctly through
a predefined interface. If the interface is lacking or is not defined
correctly, that's an entirely different problem and it's not due to hidden
state.

------
fnrslvr
> I think this is a fundamental error since functions and data structures
> belong in totally different worlds.

This is my biggest issue with the article. Blurring the boundary between
program and data is one of the core principles at the heart of computer
science, and was crucial to the works of Turing, Church, et al, that birthed
computer science as a field.

~~~
russellbeattie
That's an interesting observation, however this discussion is at an
abstraction level far removed from those basic concepts. No one is arguing
whether OOP can be compiled into logic instructions that are better or worse
than some other programming paradigm, we've got that figured out. We know how
to convert just about any system of organizing data and functions into the
billions of individual transactions that a CPU has to make to execute that
program. Copying a byte value from one place into a register, interpreting
another byte as an instruction and the next byte as the address for the result
and the next as a hardware switch... These are the concepts Turing and Von
Neumann figured out, but they're irrelevant to the modern day question of how
to organize thousands or millions of lines of code to minimize errors and
maximize understanding and efficiency.

In fact, the OOP question isn't as much about science and technology as it is
a social question concerning the best way to organize logic and information as
a profession. What's the best way to make sure programmers don't screw things
up? Is it to make sure the most incompetent developer can be somewhat
productive with simple concepts, or are those simplified concepts inherently
flawed? Or is it somewhere in the middle?

------
tabtab
The "problem" with OOP is that it was originally oversold for the wrong
reasons, and it took the industry a while to learn when and where to use it
and where not to.

Around the late 1980's OOP was hyped as a way to do domain modelling. That is,
modelling the nouns and verbs of the subject at hand (employees, invoices,
etc.) While some still defend it for domain modeling, most have found it a
poor fit for any non-trivial model.

But it did turn out a handy way to present and manage APIs to "external"
libraries or services. This is largely how it's used today.

The lessons:

1\. Use the right tool for the job, and no one tool is right for everything.

2\. Test an idea in production for a while before drawing conclusions about
what its good at and what its not.

Microservices is currently making similar mistakes, I would note. Those who
don't learn history are bound to repeat it.

------
sonnyblarney
I suggest OO is a useful abstraction because it maps well to the universe we
are trying to describe, especially with respect to state.

Rule of thumb?

Anything that can reasonably be done in FP should be done there, in 'library'
like conditions (some people use the term 'FP core' \- I think more as lib).

The point is to parse out as much of the problem into highly individual parts
that can be built/tested on their own.

Think of parsers, pre-processors, utilities like lodash, node libraries.

These things tend to be truly stateless.

Then OO for the inherently stateful stuff.

I also think the discussion might be coloured by the type of dev: UI is
fundamentally stateful. Very much so and there's no avoiding it. OO lends very
well to abstractions such as 'button' and 'text field' etc.. Backend, maybe
less so, depending on.

------
hi56793
It's always interesting to see "<insert popular paradigm/technology> sucks"
headlines. Maybe that's also intrinsic to the popularity, on the one hand this
got popular for a good reason most likely. But on the other many people might
be very careless when using that paradigm and only inform themselves
superficially - at best - about the background. That said, I really like how
JS had been tamed through OO despite the FP approach being much more
interesting.

When I use OO, my main motivation is also that it's easier to share code and
debugging becomes much more predictable. (Aahh, it's in this encapsulated part
of the code...)

------
Iv
That's a bit weird to see a seasoned programmer harboring those views. I
remember that when learning OOP I was equally confused. "I can do all that
with functions and structures already." And indeed you can. There is nothing
you can't technically do without OOP.

OOP is not a programming feature, it is a software engineering feature. It
allows for cleaner APIs and higher levels of abstractions. IMO the core
feature of OOP is less the ability to bind functions with data but the ability
to overload operators.

Sure, you can have functions that have the "this" pointer as the first
argument and have exactly the same things. You can add a bunch of flags and
have the core features of inheritance.

You _can_. Now what do you prefer to write, when finding the middle of a 3d
segment?

mid = (a + b)/2

or

mid = scalar_division(vector_add(a,b),2)

?

If you allow operators to be overloard, is there any good reason to not place
them close to your structure definition? Isn't it a good practice to force
these functions to be defined if your structure is?

The core idea of OOP is that it extends the language by allowing to build more
abstract concepts in top of lower level abstractions.

That's a core misunderstanding that I keep seeing with low-level programmers.
They want to see the implementation details of everything and refuse to
obscure some behaviors and trust the libs/compiler makers.

People who spend more time in higher level algorithms can't bother with all
the implementation details. When I do DL, I want the ability to concatenate
layers of different types (that inherit the same generic type), be able to
check their output(), manipulate tensors of floats, assign them a scalar value
or multiply them by some.

You can go a very long way without using any kind of OOP and staying at the
implementation level. I am actually in awe of how much one can do that way.
But OOP is a tool for teams to work together without knowing the details of
each other's parts and build increasingly complex abstractions.

~~~
lstamour
The problem with placing operator overloads within the definition of one
object is — which object do you look to when trying to add two different types
of objects and the assumption is TypeA.+(TypeB) is the same as TypeB.+(TypeA)?
You put them in an inheritance perhaps, but that might unnecessarily
complicate your type classification. Okay, so you use some sort of interface,
perhaps with a default implementation. Well, interfaces aren’t unique to OOP,
but they do add a bit of confusion — when looking for an implementation you
might now have 3 places to look (Possibly 4 mentions if default
implementations aren’t allowed).

Really there’s an easy conclusion to this — allowing and preferring user
defined operators or operator overloads is a hotly debated topic and one where
there appears to be no right answer — about the only conclusion you can draw
then, is that operator overload is not exclusive to OOP:
[https://softwareengineering.stackexchange.com/questions/1809...](https://softwareengineering.stackexchange.com/questions/180948/why-
arent-user-defined-operators-more-common)

I’d also suggest that with functional programs, there are limits to how much
you should cram in to one program, and that a smart way to modularize your
code would be to, for example, follow the Redux reducer pattern and build or
compose larger state objects and operations from functions that operate on
just parts of the state object—this way you isolate written changes in a
similar way to private OOP variables, where it’s just not expected (or even
“in scope”) to modify other parts of the global state. Additionally, you can
as in OOP control access to state by encouraging the use of “selector
functions” or not directly accessing state. You could make your functions take
smaller typed structs of state, really, functional programming is at least as
expressive as OOP programming. I’ll say that both allow you to make mistakes
like mix concerns, or use lots of globals, perform magic with meta-programming
or overloading, or not be expressive enough to create your own DSL in—OOP or
Functional, these concerns are in my experience shared by basically any
language.

------
oldpond
Joe was truly one of the greats. My favourite:
[https://www.youtube.com/watch?v=bo5WL5IQAd0](https://www.youtube.com/watch?v=bo5WL5IQAd0)

Thank, Joe.

------
halayli
Approaching OO with the mentality that it's easy is what makes OO looks bad.
It doesn't get treated with the same focus/attention as other methodologies.
This realization often comes in retrospect, after programming for many years
and watching how you misunderstood OO and how your abstractions improved over
time.

OO is not easy after all. You might be familiar with all the patterns which
can give a sense that you know OO well, and you end up with pretty bad design.

------
hartator
The thing is ‘filename’ is a bad example for a an object.

Of course, it should be a string. However ‘File’ object is a better example
that favors OOP. When you call ‘.read’ on a file object, you expect its
content. It doesn’t matter if it’s a windows file, unix file, an IO string, or
a web resource. When you call ‘.read’, it just work. Whereas in the functional
world, you’ll have to 4 different ‘.read’ function calls with an if statement.

~~~
tomjakubowski
> Whereas in the functional world, you’ll have to 4 different ‘.read’ function
> calls with an if statement.

A good language for functional programming provides parametric polymorphism
via traits or typeclasses:

    
    
        pub trait Read {
          fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
        }
        impl Read for std::fs::File { /* ... */ }
        impl Read for String { /* ... */ }
    

Then you can write a generic function like this:

    
    
        pub fn read_kilobyte<R>(r: R) -> io::Result<Vec<u8>>
            where R: std::io::Read {
          let mut buf = vec![0u8; 1024]; // vector of 1024 0s
          let nbytes = r.read(&mut buf[..])?;
          buf.truncate(nbytes);
          Ok(buf)
        }
    

And call it like this:

    
    
        let kb1 = read_kilobyte(File::open(...)?)?;
        let kb2 = read_kilobyte("foo bar baz quux".to_string())?;
    

No ifs in sight.

~~~
nuclear_eclipse
That looks an awful lot like OOP to me...

~~~
jonreem
One of the many difference is that there is no inheritance, and
implementations of traits can be added for types long after they are defined.
Traits in rust are not like java interfaces.

~~~
magicalhippo
How are traits specific to being a functional language rather than an OO
language though?

I mean, I don't see anything horribly functional-language-specific about your
trait example.

~~~
jonreem
It wasn't my example, but it's just disproving the notion that FP somehow
means you need to write big if statements to dispatch your functions.

Traits are not functional-language specific except in that they are often used
in FP to solve problems similar to those one might use inheritance to solve in
OOP.

------
colordrops
One thing I find problematic about C++ style OOP is that classes are just bags
of global-like variables. Unless you keep your classes very small, member data
can be mutated in a dozen places in the class in a dozen ways, and it's just a
big spaghetti mess. I understand that c++ code can be written cleanly and
readably, but in most cases it's not.

------
gpsx
I have a different opinion about why OO was popular. I think it can work well
for small programs. But it doesn't scale well to bigger problems. It just
becomes too confusing. Once people used it for bigger things, possibly because
programs got bigger with Moore's law, it can just be untenable. (But, then
again, in some places it still can make sense.)

~~~
abraae
You are right, but not wrt small vs large. Instead, OO works well for
artificial situations (classic case - UI frameworks) vs. real world situations
(classic case - person isa contractor vs. person isa employee).

As soon as you start modeling real world situations (like bill being an
employee but at the same time working a late shift as a contractor), then
you're in the world of complex stuff that single inheritance, multiple
inheritance, or anything less than insanely complex relationships requiring
tens or hundreds of underlying entities to enact just can't handle.

IMO one reason for these fruitlesd ideological arguments -is OO good? Is it
bad? - is that OO was originally sold using these real world examples. Which
anyone who's worked on a real world system knows is BS.

------
opportune
Objected oriented programming became popular because a skilled software
engineer could create an application outline/spec and then pass that on to
lower skilled programmers who would implement the spec without completely
fucking it up. It made it easy to separate out work among large teams and
write tests (ok we are going to divide up the work between these
implementations, each of which gets its own set of unit tests for each
function) and standardized some of the high level interactions ahead of time
(see: convert spec to code). It was never meant to be a surgical scalpel that
a highly skilled developer could use to attain high individual productivity.

Also I highly disagree with the assertion that "Data structure and functions
should not be bound together". I would argue that if you are using any
sufficiently complex or niche data structure that is not part of some standard
library or a primitive, you absolutely do want to bind functions to the
datastructure. It is much better to define a data structure with a set of
logical human-readable functions for what you want to do with it than to keep
it as some collection of primitives or standard lib datastructures you pass
around everywhere. Let's say you are writing a spell checker. You _really_
want to abstract away all the ugly details of the trie and any other helper
datastructures. You also may want to create an interface for some "word
library" that you can extend with different solutions to test e.g. performance
(if you are trying to optimize for caching). Object oriented programming
really simplifies things in a scenario like this.

------
gfiorav
OO is good for programmers to model problems, not for yielding the best
result. I find that thinking in terms of functional code (info being passed
from one place to another; not diluted into a lot of objects with state)
yields the best result in terms of stability and lack of bugs

------
bartimus
It just makes sense to me:

    
    
      * Domain = logic + data
      * Application = logic + data
      * Object = logic + data
    

Nothing wrong with using functions and data objects. But they're often still
interdependent. A class just makes it official.

------
georgeecollins
What are the concepts today that smart people are convinced are the "right"
way to do a thing that later will seem dubious? \- Microservices? \- Agile
development?

I feel like I am not trendy enough to know what the latest development
religions are.

------
olodus
> Conventional languages (like C or Pascal)...

I've always heard of Pascal as "the Java before Java" or atleast quite an OO
language. And since I knew Java I never thought to even go and look at it. Did
I miss something here?

------
lincpa
FP and OO are overly complicated, and it is not feasible in large industries.
It is also a kind of production method that emphasizes personal technology in
hand workshops. Personal technology greatly affects product quality and
extremely unreliable production methods.FP and OO are actually taking a
detour, highly embellished and ineffectual, and produce all kinds of fail.
Most OO systems are just simulations of real-world surface phenomena, and the
whole system, like a mess, I think it is not good method of OO to simulate the
real world, but to design it correctly with an abstract refined data model as
a prototype. For example, the ggplot2 of the R language, the system is clear,
with the perfect data model as the prototype. So a good OO system is more
inclined to a data flow system, and I think Ggplot2 is more likely to be a
data-driven plot system if OO was not in vogue at the time.

Excessive application of OO and FP design patterns, in addition to increasing
complexity and error probability, reduce performance, without any benefit.
Complex networks of relationships between objects in the OO system are also
difficult to maintain.

I tend to construct systems with the simplest concepts and the most basic
techniques, syntax, and functions. Used to implement my mind, The Pure
Function Pipeline Data Flow is the simplest, stable, reliable and readable..
There is a great poet Bai Juyi in China. even illiteracy understands and
appreciates his poetry. I hope that my code can be understood by the junior
programmer even in the most complicated system.

For me, programming is the process of designing a data model that is simple
and fluent in manipulation. More than 80% functions of my project is ->>
threading macro code block, each step is simple, verifiable, replaceable,
testable, pluggable, extensible, and easy to implement multithreading. The
clojure threading macro provides language-level support for
PurefunctionPipeline&Dataflow.

[https://github.com/linpengcheng/PurefunctionPipelineDataflow](https://github.com/linpengcheng/PurefunctionPipelineDataflow)

------
raxxorrax
OO is basically about modeling data. It doesn't make any statements about
procedures, even if it includes operations on said data. If you expect more
from OO, you will be disappointed.

------
ne01
To me OOP is complex and complexity is the enemy of writing good code.

Also simplicity is relative.

Most of us like OOP because we have learned it and invested time in it and
most probably defined by it.

------
lr4444lr
Is it just me, or do these web articles written in plain <p> and <h*> tags
just have some magical command over your attention as carrying some authority,
like the author is so focused on his content that he doesn't a single mental
cycle to waste on any more than the bare minimum UI to convey it?

------
0815test
Should be marked (2000), and the bulk of the text is probably earlier than
that.

------
cubano
This is so obviously old-school flame bait that I can't understand why we are
even discussing it, unless the idea is to go meta with its discussion.

~~~
DonaldPShimoda
The author died a couple of days ago in (I think) a bicycling accident. I
imagine people are going through his old posts as they remember him or hear
about him for the first time.

------
IOT_Apprentice
Joe passed away last week.

------
TheAsprngHacker
I agree with a lot of the criticisms in Dr. Armstrong's article, but I would
say that they mainly affect languages such as Java that enforce OO; multi-
paradigm languages that allow for OO but don't force it on you don't have
these problems. I believe that OOP has valid uses.

> Objection 1. Data structure and functions should not be bound together

In languages like OCaml and C, one defines types separately from functions.
Sometimes, however, I find that a function is best associated with a
particular type. In OCaml, that's one purpose of modules. (The convention is
to call the main type "t.") However, this organization shouldn't be forced on
you.

> Objection 2. Everything has to be an object.

This objection only applies to languages like Java that force OO on you. In
OCaml or C++, I don't have to use OO, but I still can if I want to.

> Objection 3. In an OOPL data type definitions are spread out all over the
> place.

Armstrong complains about having to decide what to inherit from when making a
Time object. IMO this is a strawman; in Java, classes inherit from Object by
default and I don't see any inconvenience. I do agree that it can be annoying
that Time has to be object-oriented, and this is where the benefit of OO being
voluntary comes in.

> Objection 4. Objects have private state.

I either disagree with or don't understand this objection. Dr. Armstrong lists
three ways to deal with state.

His third option is to use pure functional programming. I see both the beauty
and practical value in making functions be functions in the math sense, that
is to say, mappings from elements of the domain to elements of the codomain.
Then, you get equational reasoning. I am sympathetic to this idea.
Nevertheless, languages like Haskell aren't for everybody.

His second option is to control access to mutable variables with scope. You
can implement a pure function in an imperative way, and that can be a form of
encapsulation. This is a good approach, too.

Dr. Armstrong's first option, which he says is the worst, is the idea that
objects maintain hidden state which people control via methods. To me, this
isn't about state, but rather encapsulation and abstraction. The idea is that
when one, say, inserts into a hash table, the person doesn't think about the
implementation ("hash the key and walk down the corresponding bucket"), the
person thinks of the abstract problem domain ("map this key to this value").
The hash table interface hides its internal state behind the methods. Even in
a programming language like C, one writes functions that operate on, and
mutate, structs to abstract things away.

Although the second option is good, I see it as orthogonal to the first
option, not a superior alternative. If you are going to confine state to
within a function, ultimately, don't you have to do mutations on the local
variables, and therefore call some kind of method or impure function on them?
However, perhaps I am misunderstanding this fourth point.

A lot of arguments, including this submission, pit OOP against FP and argue
that FP is superior to OOP. Even though I use FP, I also think that OOP can be
useful. They are not opposites and don't have to conflict.

OCaml is infamous for its object layer, yet I ended up using it for a project.
I wanted to describe UI "widgets" such that I could compose arbitrary
different types of widgets. Before deciding on objects, I had considered
first-class modules or a record of functions, but ultimately decided that
objects were the most readable choice.

In my OCaml program, I mainly viewed OOP as a way to abstract different types
into a common idea. Widgets are all different, and they have different
behaviors, but they all have a position in space, a length, and a width. One
can also reposition them (mutation!). To me, the purpose of OO is to express
ideas such as these. I note that when I used OOP here, I cared less about
state and more about the abstraction of what a "widget" was, via dynamic
dispatch. It is bad when the language forces OO on you, but it has valid uses.

------
cottsak
Largely agree with this except the initial objection "Data structure and
functions should not be bound together" and the "Why was OO popular?" which is
missing the response - because using the notion that "objects" from the real
world encapsulate state and behaviour was not only a solid premise, but it was
an attempt to interface the computer world with the real. This was not a
failed concept. It makes some sense. Just very few teams find the discipline
to model their core domain this way.

Which brings be back to the original objection. I think this is true most of
the time, except when it's not - which is your core Domain Model. The first
1/2 of the Blue Book[1] lays out straightforward means to arrange code,
functions, data/state and related behaviours in a way which can be managed and
maintained over time. This is pretty important as most folks who've spent any
length of time maintaining vast applications will know that it's incredibly
hard to reason about a first-class concept in an application without clear
boundaries around said concept, it's structures and it's behaviour. Most of us
are unlucky and find this scattered across the landscape. Few applications
take the focus to "model" these concepts clearly.

Does this modelling have to be done with "Domain Model", or DDD, or something
else that can be loosely coupled with OOD - probably not. But another
developer absolutely has to be able to reason about said structures and
behaviour. They have to be able to read it easily and grok it quickly. And
having done that, they don't want to be surprised by some distant missing
element, 20 calls or 1000 lines or 15 modules (repos, submodules, etc, etc)
away! This is possibly the biggest time-sink and therefore "cost" of
development. One could also take this further and postulate that about 1/2 of
us are employed as a direct result of applications whose core concepts are so
poorly designed or hard to reason about, that a massive volume of work (time?)
is dedicated to unwinding the ambiguity that results.

I don't want to suggest that OOP or OOD/DDD/{other modelling process} would
necessarily fix this, but the attempt to clarify and find a means to make
modelling these critical concepts easier and less costly is admirable IMO.

It's ok if your infrastructure takes a different approach, or is "functional"
or "dynamic" in nature. If your test suite uses completely different patterns
and paradigms because the tooling makes that easy then - awesome! But if the
core model/concepts of your application are hard to understand, reason about,
and therefore maintain, then you're pretty fucked.

OO doesn't "suck". It's spirit is just largely lost and like many other things
in life, it's been hijacked and mutilated into something many of us come to
loathe because we've never seen it deliver on the promises. I guess we will be
having this conversation again in another decade about something else that's
hugely popular right now.

[1] [https://www.amazon.com/Domain-Driven-Design-Tackling-
Complex...](https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-
Software/dp/0321125215)

------
jasoneckert
I definitely C the arguments he makes, and agree with them.

------
profalseidol
> State is the root of all evil.

Both in programming and in real life haha.

