
When FP? And When OOP? (2013) - ausjke
http://raganwald.com/2013/04/08/functional-vs-OOP.html
======
alkonaut
I think these concepts are often conflated. One thing I miss most in OOP and
appreciate most when doing FP is ADT's, when I can represent

    
    
      Type payment = Invoice(Address) | Card(CardDetails) 
    

And then match/switch on those things, while the compiler will tell me if I
did something wrong such as not handle one case. Adding a third type of
payment (Cash) is as simple as adding "| Cash" to the definition - and the
compiler will tell me exactly where I haven't handled cash payments.

A simple smell test for a programming language is this: if describing the
above takes more than the above code - your language has a wart. If you try to
describe this properly in C# you'll be writing an abstract outer class and N
concrete inner classes, plus boilerplate for comparisons and so on. You'll be
looking at likely 50-100 lines of code where _no single line of code actually
shows whan what you are modeling_. This is the _terrible_ part of OO. I'm sure
there are a few cases to show an FP weakness handled elegantly by OO - but I'm
equally sure those cases are fewer.

Also, there is nothing particularly "FP" about ADT's. They are just common in
FP languages. Any OO language with a sum type and exhaustive matching can do
this. But typically, such as for Java, C++ and C# they don't (yet). There are
languages that support this that aren't FP (but not necessarily OO either)
such as Rust and Kotlin.

~~~
chriswarbo
> Adding a third type of payment (Cash) is as simple as adding "| Cash" to the
> definition

This is the "expression problem"
[https://en.wikipedia.org/wiki/Expression_problem](https://en.wikipedia.org/wiki/Expression_problem)

In FP with ADTs, anyone can write new functions for a datatype. Yet, as you
say, adding a new case requires modifying the definition and existing
functions.

In OOP, anyone can add a new case, by defining a new subclass. Yet writing a
new method for that class requires modifying the definition and existing
subclasses.

One of these isn't always better than the other, so it's useful to have both.

> If you try to describe this properly in C# you'll be writing an abstract
> outer class and N concrete inner classes, plus boilerplate for comparisons
> and so on.

This overhead is due to emulating one approach using another. It would also
take a bunch of boilerplate to implement subclassing with ADTs (e.g. a sum
type to describe the methods, a smart constructor to implement the
inheritance, etc.).

(Personally I prefer ADTs too, but they're not a magic bullet)

~~~
unhammer
OCaml has ADT's, but also polymorphic variants[0] which are open, and let you
say "these alternatives or more", or "these alternatives or less":
[https://v1.realworldocaml.org/v1/en/html/variants.html#polym...](https://v1.realworldocaml.org/v1/en/html/variants.html#polymorphic-
variants)

It's the main thing I've missed from OCaml when programming in Haskell (you
can sort of do something like this with typeclasses, but I much prefer the
OCaml solution).

~~~
dan-robertson
Ocaml also has open variants where one can add cases to an existing sum type
(essentially this is like exceptions but less special) and it’s objects are
like a dual of polymorphic variants where you get a record which you may add
fields to. Neither of these features is as commonly used as polymorphic
variants however.

------
jameslk
One important consideration I think that's missing here is how the computation
over the data is processed. Immutability, a hallmark of FP, has a small
performance hit on both computation and memory[0] but allows for computation
to be divided more easily via referential transparency[1].

For applications that will scale horizontally, such as web apps, I think FP
makes more sense, since computation over data can spread across multiple
servers/processors. Where as for applications that cannot scale horizontally
but must be performant, such as game engines, OOP seems to make sense since it
makes mutation easier to handle by hiding it with encapsulation.

I have a hypothesis that multiple core processors and cloud computing led to
the current surge in popularity of FP.

0\.
[https://en.wikipedia.org/wiki/Functional_programming#Efficie...](https://en.wikipedia.org/wiki/Functional_programming#Efficiency_issues)

1\.
[http://wiki.c2.com/?ReferentialTransparency](http://wiki.c2.com/?ReferentialTransparency)

~~~
gnulinux
Imho this kind of thinking is wrong. I believe in every large enough program
such as a web server or game engine there will be parts that are suitable for
fp and suitable for oop. A web server (of a large enough company) and a game
engine are complex enough programs that they do not include one type of data
flow, so why would we need to use only one type of programming to formalize
that data flow?

~~~
jameslk
Applications large enough will likely contain both paradigms, yes, but the
processing constraints of the application will likely dictate the appropriate
methodology to use dominantly.

For example, I always found it ironic that the JavaScript library Immutable.js
uses OOP heavily, but I'm guessing this was for performance reasons.

~~~
sshine
Yes, every compiler/transpiler inevitably must address its underlying
(virtual) machine's primitives.

------
gambler
1\. What you see in Java is not OOP. It's static, late-bound class/interface
oriented programming. Mostly just a set of subtly bad ideas.

2\. Real OOP and FP do not operate on the same level. Functional, logic and
imperative programming are about language design. OOP is about system design.
C++ and Java poisoned the concept. Listen to Alan Kay. Note that he openly
admits that "objects" as they were implemented in Smalltalk are _too small_.
Good objects are probably somewhere in between Ruby/JavaScript object and ad a
microservice in size/complexity.

3\. You can implement an OOP system in an FP language. Elixir/Erlang are a
good example.

\---

Side note: most of what Kay said about system design was validated many times
over. If you think you know better than him simply because you use command
line and functional programming, while he talks about graphical interfaces and
OOP, you are missing the point, big time.

~~~
stcredzero
_Mostly just a set of subtly bad ideas._

This describes most programming languages. Actually, on average it's usually a
bit worse. It's a set of nifty-on-the-surface ideas which are also subtly bad,
which put together result in emergent really-bad.

Programming languages are like bands or sports teams. The constituent parts
might not be the "best" field leading examples, but put together the whole
gestalt can still be awesome. It can help if there's one part which is
attention getting, but the whole thing has to "jell."

 _You can implement an OOP system in an FP language. Elixir /Erlang are a good
example._

Lisp as well.

The biggest FP/OOP idea I've seen in my career has to do with awareness around
dataflow. If your app is actually about dataflow, then dataflow and explicitly
representing the relationships is key. I've seen lots of bad OOP thrown at
dataflow, where only few of the relationships are explicit, and you "just have
to know" how information gets from one part to another.

~~~
0db532a0
Could you recommend any articles outlining what you’ve said?

~~~
stcredzero
That's all from my personal experience, including consulting.

------
austincheney
> A well-crafted OO architecture makes changing the way things are put
> together easy.

I agree and its why I don't like OOP. Either you are thinking in terms of
putting things together or tearing them apart. Complex means to intertwine or
_put together_. OOP thus increases complexity and a well defined OOP system
makes increasing complexity easier.

I, personally, prefer simple. Simple systems takes more work up front, but are
easier to read, reason about, extend, and maintain.

~~~
mikekchar
While a lot of people make objects for the sake of making objects, I think it
is instructive to think _why_ you want to make objects in OO. As another
poster has said, what if I had structs and functions with multiple dispatch?
Why would that not be better than OO?

People tend to think of OO as a way of encapsulating data and then providing
access to it. I rather think of it as a way of encoding program _state_. Often
you have multiple operations that you want to perform on the same program
state. It is nice to group that code together. Indeed, we have a computer
sciency word for that: cohesion.

In FP, too, we do exactly the same thing. Take a look at any modern FP program
and you will see that it is usually broken down into modules that represent
program state. The modules contain code that operates on that state. We do it
for the same reason we do it in OO: it creates good cohesion.

Getting back to my original question: why not structs with multiple dispatch?
Actually, I wouldn't mind that at all (what's not to like about multiple
dispatch?). However, the problem, I actually have is the struct. It's the same
problem I have with the way many people approach OO: they are thinking about
the code from the perspective of how to encapsulate data, not about what
functionality they want. They are thinking about raw data instead of program
state. A struct is a fine place to store data, but it's not the first place
you want to go when you are doing design.

Instead you want to build your functionality and identify where you have
commonalities in state. Then you should start thinking, "Hey these are using
similar state. Are they related? Would it be more cohesive if I grouped them
together?" Of course, it would be kind of strange to do that all the time.
Most experienced programmers have a pretty good idea what program state they
need to work on before they start. They can design data structures that are
likely to be needed ahead of time. However, the priority should be to adjust
the data structures to match the needs of the program state and to create more
cohesive programs -- not to provide rigid boundaries separating your code.

Of course, nothing I've said is much different than programming in FP, and I
wish that were not a surprise. OO and FP are not really so different. It's
pretty easy to make the exact same mistakes in FP as you can make in OO. I
sometimes feel like FP is just not popular enough to have widespread bad
practices (like writing entire applications using only free monads).

~~~
dnautics
> People tend to think of OO as a way of encapsulating data and then providing
> access to it. I rather think of it as a way of encoding program state. Often
> you have multiple operations that you want to perform on the same program
> state. It is nice to group that code together.

That's very consistent with the original definition of OO, which hardly anyone
uses much anymore. And erlang and elixir (and Ruby if you follow Gary
Bernhardt) both fps do that quite well, via the actor model. Where OO starts
to fail is when you gave to make a decision between two objects, e.g. "knight
attacks mage" is the "attack" function a member of knight or a member of mage?

Grouping functions by what state they touch is basically just namespacing,
common between OO and FP.

~~~
hurflmurfl
Can't we just create a separate class, like a `Battle`? And then do
`Battle.between(knight, mage)` where Battle would handle the negotiations
between the two classes? Maybe producing a result without mutating the two
entities we pass as arguments. I feel like it depends on what the business is.
Is it tracking wizard adventures, fighter exp calculator or distributed battle
log.

~~~
choward
> Can't we just create a separate class, like a `Battle`? And then do
> `Battle.between(knight, mage)`

Exactly. And at the end of the day all you're really doing is defining a
function. Why do you need a class at all? Is it just because the language
you're using doesn't allow standalone functions? If you're defining a static
function that has no state on a class, you don't need the class at all. It's
just a function that you want.

battle(knight, mage)

~~~
kingdomcome50
The missing observation here is the `Battle` or `Collide` operate at a level
of coordination. It is at this level where FP shines.

Simply adding an `addDamage` method to the above system exemplifies where OOP
shines. The above method works at lower, domain level, where the semantics of
adding damage are specific to the object to which damage is being added.

The point here is that _both_ paradigms should be employed.

------
sifoobar
I'll take structs and multiple dispatch [0] over both of those any day. Raw FP
is too primitive and OOP mixes too many concerns, multiple dispatch strikes a
nice compromise between the two and offers additional flexibility.

While Common Lisp's CLOS is often referred to as OOP, it's definitely less
coupled than single dispatch.

[0]
[https://gitlab.com/sifoo/snigl#functions](https://gitlab.com/sifoo/snigl#functions)

~~~
AnaniasAnanas
Please do correct me if I am wrong, but isn't multiple dispatch basically
typeclasses + pattern matching?

~~~
sifoobar
But that would still be single dispatch, right?

Multiple dispatch means functions are generic, and all arguments are
considered when deciding which implementation to call.

Snigl supports both multiple dispatch and pattern matching, as does Common
Lisp.

~~~
hesselink
Haskell type classes by default pick the implementation based on a single type
variable in its signature, but it can occur in multiple positions (including
the return type). A commonly used language extension (multi parameter type
classes) extends this to an arbitrary number of type variables.

------
exergy
A tangent here, but a place where object-orientation works better than
anything else is in simulations of the real world. Thus, nothing to do with
databases, and in fact it is referred to as object oriented modelling (OOM)
rather than OOP.

The way it works is that, let's say you are modelling a pump. The pump is made
up of a motor, a shell, the fluid with its own conservation equations etc. In
such cases, inheritance and aggregation are godsends that allow us to quickly
compose real-life objects as aggregates of easily modified and understood sub-
components. Also, once you've taken these sub components to a point where you
trust them, there is no further need to spend any time thinking about them.
They are there, and you trust them, and you can reuse them and combine them
with confidence.

More than anything, it gives us a great way to _think_ about the modelling
problem. The success of Modelica speaks to the superiority of this paradigm
over things that came before it.

~~~
humanrebar
Eh. Most processes in the physical world are reciprocal and most relationships
bidirectional. OOP wants you to add a method to _one_ class. So you're either
figuring out which billiard ball gets the 'collide' method call or you end up
modelling very abstract concepts like CollisionPolicy and the "real world
modelling" starts looking more and more like functional abstractions
OnlyWithMoreNouns.

~~~
dtougas
If you are giving your ball a collide method, or creating a CollisionPolicy,
then you are probably doing it wrong. Therein lies a huge part of the problem
of OO design: modelling is difficult, and lots of us get it wrong.

Just because you use FP or some other paradigm does not automatically mean you
will get it right. I have seen poorly modelled solutions written in many
different languages. I have also seen elegant solutions implemented in a
variety of forms as well.

I think that having some depth of experience in a language, as well as a solid
grasp of the problem, and perhaps some anal retentive tendencies (i.e. an
innate desire to keep things very structured and consistent), can go a long
way to solving problems elegantly :-)

------
tasubotadas
Usually, I follow a general rule of thumb:

* For domain language (entities, services, value objects) use OOP as it is better at communicating intent

* For data processing and low-level algorithms (usually happens inside OOP methods) use FP style as it is less error-prone.

~~~
gardaani
I found an article which has similar rules:

* Use FP when you need to build something purely computational.

* Use OOP when you want/need to build something generic for other people to use.

* But if you are application or game developer, ECS way of thinking brings a lot of benefit to the table.

[https://hackernoon.com/when-not-to-use-
ecs-48362c71cf47](https://hackernoon.com/when-not-to-use-ecs-48362c71cf47)

~~~
yogthos
I have to disagree here. It's much easier to use data driven APIs than OO
ones. With the former you get what you see and there are no additional rules.
You call the API, it produces some result that you can use in any way you
like. Meanwhile, objects have behaviors and internal state that you have to
understand because each object is its own ad hoc DSL with its own rules and
behaviors.

------
millstone
What do others think of "SQL as a very functional language?"

The insight that businesses decouple data from procedures is right. But SQL
has poor support for functional hallmarks such as recursion, functions as
first-class values, etc. SQL is not functional but declarative: it's less
Haskell and more Prolog.

~~~
pdonis
_> What do others think of "SQL as a very functional language?"_

I think the author is using "functional" in a different sense from its usual
use nowadays. Basically I think he just means that with SQL, you focus on the
operations you're performing (i.e., functions), and the functions are only
loosely coupled to the actual data, since you can write many different
functions (SQL statements) on the same database, and the same SQL statement
can be applied to many different databases with different underlying storage
architectures. But of course, as you note, SQL being "functional" in this
sense does not mean it supports more recent features associated with
functional programming.

~~~
coldtea
> _I think the author is using "functional" in a different sense from its
> usual use nowadays. Basically I think he just means that with SQL, you focus
> on the operations you're performing (i.e., functions), and the functions are
> only loosely coupled to the actual data, since you can write many different
> functions (SQL statements) on the same database, and the same SQL statement
> can be applied to many different databases with different underlying storage
> architectures_

That's called "declarative" \-- not "functional".

~~~
pdonis
Huh? To me, "declarative" means focusing on just stating what you want the
result to look like, not what operations need to be performed to obtain the
result. I agree that SQL could be considered declarative as well as
functional, since an SQL statement has aspects of both. But I don't see how
"focus on the operations" is declarative.

~~~
coldtea
> _To me, "declarative" means focusing on just stating what you want the
> result to look like, not what operations need to be performed to obtain the
> result_

And that's exactly what SQL does.

You specify what the result should be like "I want the result to have those
two sets common elements, but only items where this column is larger than 10,
and I want it grouped by these 2 columns and ordered by this over column", and
now how to get to it (e.g. open these files, read those indexes, load a hash
table with matches, iterate over them to find those where column > 10 etc).

GROUP BY x and WHERE X > 10, etc. are not "the operations performed to obtain
the result", are high level descriptions of the result we want.

In fact in CS courses, SQL was one of the canonical examples of declarative
programming (despite some nits to the premise, e.g. the ability to specify use
of indexes etc).

~~~
pdonis
_> And that's exactly what SQL does._

So an SQL UPDATE statement is purely declarative? "Update" sure looks like an
operation to me. Similar remarks would apply to DELETE or INSERT.

For queries, I can see your point of view, yes; but even for queries, SELECT,
GROUP BY, etc. can be viewed as operations just as well as descriptions of the
result set. (WHERE, I agree, doesn't really look like an operation.)

 _> GROUP BY x and WHERE X > 10, etc. are not "the operations performed to
obtain the result", are high level descriptions of the result we want._

Again, I think this is a matter of point of view. GROUP BY isn't a low-level
operation, sure; but that doesn't mean it's not an operation, or can't be
viewed as one. High-level operations are still operations.

 _> in CS courses, SQL was one of the canonical examples of declarative
programming_

I'm not trying to dispute this. I'm saying that given how the author of the
article is using the term "functional", it seems to me that SQL could be
considered "functional". But I've already noted that the way the author of the
article is using the term "functional" is probably not the way that term is
commonly used.

~~~
coldtea
> _So an SQL UPDATE statement is purely declarative? "Update" sure looks like
> an operation to me_

The Update statement tells "set this column to this value". It's totally a
declaration of intent.

It doesn't specify anything about how the update is going to happen behind the
scenes (open this file, use this tree structure, traverse, find the nth entry,
write this value to the disk, and so on).

(Databases also support PL/SQL which is more imperative in nature, but here
we're talking about SQL the query language).

> _but that doesn 't mean it's not an operation, or can't be viewed as one.
> High-level operations are still operations._

Operation in the context of declarative vs non declarative is not about high
vs low, it is about whether the language has the user explicitly define
control flow.

In the "group by" the user just tells to the DB that they want the results
grouped, they don't specify any control flow, or tell how that grouping will
happen.

~~~
pdonis
_> The Update statement tells "set this column to this value". It's totally a
declaration of intent._

"Set this column to this value" sure looks like an operation to me.

 _> It doesn't specify anything about how the update is going to happen behind
the scenes_

Neither does the expression y = f(x). But that expression is the canonical
example of a function, not a declarative statement.

 _> Operation in the context of declarative vs non declarative_

Is whatever you are defining it to be, I get that. But I don't think the
author of the article is using words the same way you are.

------
samuell
I have recently came to like the following distinction:

\- Let data be relatively static, immutable, objects (just as the post
suggests)

\- For functions which are merely read-only views of this data, implement them
as methods on the class (An example would be "Volume()" on a data object
representing a geometric object)

\- For functions that changes the original data on data objects, implement
them as separate functions (can be encapsulated in asynchronous black-box
processes ala flow-based programming if you like), returning new, immutable
instances of the objects they operate on.

This allows keeping data objects immutable, while still allowing you to gather
certain frequently used calculations on the object itself, for code
readability / maintainability, as long as they are read-only.

------
diminoten
The problem arises when you try to couple an OO language with a FP design (or
vice versa). You end up leaving large portions of the language on the floor,
and are constantly "inventing" ways to solve problems that you'd get for free
if you just did things the way the language was built to do them.

The reality is we should be much more flexible with what languages we choose
to solve problems, if we want the flexibility to best match our problems to
the right design paradigm.

Failing that, it's better to go with one "kind" of programming, the one that
fits the language. At that point, we're hunting consistency, not the platonic
ideal of the solution.

Unrelated, but it seems like people here were sold OO as magical or somehow
"easy". It's damn hard to get an OO design right, and if it's not working for
you, it's probably you and not OO. You probably missed something, which is not
only reasonable, but expected. That's where the experience, skill, and
expertise come into play.

------
asplake
> The central tenet of FP is that data is only loosely coupled to functions.
> You can write different operations on the same data structure, and the
> central model for abstraction is the function, not the data structure.

Is this accurate? I would see it more as FP separates the two abstractions
that OOP (for better or for worse) would have us keep together.

------
maweki
I think about using OOP only in terms of the structure of my data. Is my data
more easily represented using actual pointer loops or is it a tree structure?

With immutable objects you can't easily create longer loops in data (self-
reference is okay though).

Is my data a tree of some notion, I always go for FP.

------
0x445442
I think OOP is better suited when state needs to be maintained in memory over
time. User interface development is one obvious use case and I think OOP
really gained traction during a period when thick clients were the norm.

However, when thick clients gave way to web apps (ignoring SPAs for the
moment) the industry still tried to pound the square OO peg into the round
backend hole. Most of that backend code is just transforming data between a
database and either a browser in the case of a web app or another client in
the case of web services, applying business rules along the way. I think this
class of software is much better served by the FP model.

~~~
yogthos
FP works much better for UIs in my experience. Take a look at Reagent as an
example [http://reagent-project.github.io/](http://reagent-project.github.io/)

With Reagent, you have reactive atoms containing your data and components
subscribe to them. Whenever the state of the atom changes, the UI is
automatically repainted. You basically end up with a classic MVC pattern where
UI components dispatch events that update the sate, and observe the changes
via subscriptions.

The advantage over OO is that the state is easily decoupled from the UI, and
you can observe the entire state of the UI via data. Since the logic lives
outside the UI components, you can also do testing at event level which tends
to be a lot more stable.

~~~
ceronman
I love how Reagent works and I do think it makes interactive applications
simpler and easier to reason about.

That said, I think FP still has one disadvantage which is performance. This is
because the underlying computer is 100% imperative and there are still a lot
of work to make FP patterns efficient.

FP works well for interactive UIs because the number of objects that change in
GUI is very small, so performance is not an issue. But take a video game, for
example, then you have thousands of moving objects that are updated at 60FPS.
Once you get that you notice is really hard to write the logic as a set of
immutable state changes, object allocation becomes a bottleneck and then you
discover that OO is more suitable, at least for now. Maybe later there will be
ways to efficiently translate the FP patterns into the imperative CPU models.

~~~
yogthos
That's a bit of a misconception about FP though. Immutability is just the
default, and you can opt into local mutability when you need to. Clojure
transients are a good example of this. So, in a case of a game, I would create
local mutable sections for the tight loop.

It's also worth noting that modern hardware hasn't been using imperative CPU
models for a long time, and it provides an emulation layer that languages like
C target
[https://queue.acm.org/detail.cfm?id=3212479](https://queue.acm.org/detail.cfm?id=3212479)

------
ken
> And yet, we embrace objects in our applications. Is this just fashion?

I think that's 90% of it, actually. In no case in my life have I seen a
company starting a new project say "We're going to examine the available
{programming languages, databases, operating systems, ...} and pick the one
which is most appropriate to our problem". It was definitely never even
considered after more than 3 lines of code have been written.

Even upgrading from one version of a language to a newer version of that same
language is, usually, like pulling teeth.

Thinking back over the past several companies I've worked with, reasons for
picking a programming language included:

\- $(first_dev) wanted to learn a new language

\- $(first_dev) _didn 't_ want to learn a new language

\- Company policy says we must use $(lang) (even though no other software runs
on the same platform, or has anything at all in common with this new project)

\- $(founder) used to work for $(compiler_company)

\- We googled for a library to do $(task) and first one we found was for
$(lang)

I don't know what to call this, but it isn't engineering. My only problem with
calling it fashion is that I'm afraid it would seem disrespectful to the
fashion industry.

------
z3t4
If you take pure functions, and "side effect free" mantra, and apply it to OOP
you get a pretty good mix. For example, instead of saving a file to _the_
file-system, you save the file to _a_ file-system. A class/prototype can only
mutate it's own object. If possible - a function should only use it's
parameters and internal variables, eg. not access global/module scope.

------
phaedrus
I disagree with his conclusion which equates the database to functional
programming. Relational algebra is a third paradigm on the same level as, and
different from, OOP and FP. I remember seeing an article pointing out that the
database is the "mutable global variable in the middle of your functional
program". Also both functional programming and object oriented programming
make much more use of hierarchical organization and implementation hiding than
do relational databases.

~~~
snidane
This is exactly where typical pure lambda-calculus based programming falls
short. Lambda calculus afaik doesn't have the concept of IO, so it has to be
bolted on in the programming languages as an afterthought.

I believe more powerful formalism has to be used. For example pi calculus or
petri nets. In both you can represent the 'global variable database' as a
process sitting somewhere and you communicate with it using channels or as a
message passing in general. Lambda calculus is too constraining, because
everything is a deterministic function and it can't model exactly this
scenario. Another use case hard to model in lambda calculus is time. Eg. run
some function after N seconds. Or plain 'sleep' function.

When you now look at programming languages in terms of, let's say, Petri nets,
you see clearly how OOP (Alan Kay's message passing) is the part where you
send messages between processes and FP is contents of a particular process.

Other than that, there are two types of OOP I noticed. 1\. Alan Kay message
passing. Originated from people trying to implement simulations 2\. OOP as
modules (Java, C++), aka 'Dog implements Animal' OOP. In this paradigm people
try to come up with ontologies, ie. hierarchical models of data dependencies.
Instead of using actual modules, people use classes for some reason. Then you
can see module-like features implemented on classes, eg. private and public
modifiers of methods.

'Dog implements Animal' should be modeled as 'Module Dog depends on module
Animal'

------
willtim
The most useful part of OOP when building large systems, is its inherent
support for first-class modules. This is the one feature I miss when working
in most FP languages.

~~~
scns
Check out the module system of OCaml/ReasonML

~~~
willtim
Yes it's very good, but still somewhat stratified. Checkout the 1ML project
for something even more unified: [https://people.mpi-
sws.org/~rossberg/1ml/](https://people.mpi-sws.org/~rossberg/1ml/)

------
kingdomcome50
I find most discussions of this nature to be completely missing the point.
Semantic arguments about the specific syntax required to accomplish some goal,
or comparisons of which approach works better for a _single_ given use case
are silly. We get it, the expression problem exists. The fact of the matter is
that FP and OOP work on different levels. Higher-levels favor FP and lower-
levels favor OOP. That is, coordination is best-implemented via FP and
enforcing rules is best-implemented as OOP. Arbitrarily choosing a single
approach ensures poor system design.

For example, as many have pointed out, methods like "collide" or "attack"
don't really make sense to have on a single object. This is because this
method works at the level of coordination, and therefore _should_ be
functional. Alternatively a method like "addDamage" method works at the domain
level were it enforces rules/state change.

We can easily apply the above observation to system architecture and conclude
that the outer-most layers of an application (services) are best-implemented
using a FP approach and the inner-most layers (domain) are best-implemented
using OOP.

It does not surprise me that most applications adopt FP. Because most people
think procedurally and start applications with a top-down mindset, the outer-
most layers (use cases) tend to be modeled first where a FP approach makes
sense. This then just carries through the rest of the application.

~~~
AnimalMuppet
> It does not surprise me that most applications adopt FP. Because most people
> think procedurally and start applications with a top-down mindset, the
> outer-most layers (use cases) tend to be modeled first where a FP approach
> makes sense. This then just carries through the rest of the application.

Did you mean "OOP" instead of "FP" here? First, I think more applications
adopt OOP than adopt FP. Second, I find it hard to go from "most people think
procedurally" to "therefore they wind up at FP".

~~~
kingdomcome50
I did not. Simply using objects does not mean one has adopted OOP. OOP means
modeling behavior _with_ data, not _around_ data. It is something that must
done be intentionally, not a product of your choice of language (although can
certainly play a factor).

I would suggest that most systems adopt a procedural style where objects are
more or less bags of data being passed around to functions/methods. This
generally fits the mental model of how people think. That is, in terms of
input -> output. OOP blurs that simplicity because each unit of behavior
(method) also has a surrounding context (properties) that may be affected as
well.

At the end of the day the difference between Object.DoSomething( data ) and
DoSomething( object, data ) is just semantics (look at how python implements
classes for example).

------
sidcool
This is a good point. Strictly adhering to just one style, either OO or FP,
seems very limiting. Instead, this blog suggests guidelines. My favorite line
from the post:

"Those that are unlikely to change but are subject to being operated upon by a
changing cast of entities should be written in a more functional style, while
those things that change relatively often can be written in an OO style"

~~~
innocentoldguy
I can understand that line of thinking, but I don't know that I agree with it.
At least not fully. The biggest problem with OOP from a data perspective is
that it shleps state around and that state can change in goofy ways,
especially when you introduce threads. I think this is where immutable
functional programming really shines. I do realize there's a lot more to
consider though when architecting a particular solution.

~~~
sidcool
Totally agree, I would introduce immutability in OO to address Concurrency
issues. In short, picking the best of both worlds.

Where the conflicts arise is when we have stateless functions. It's a bad
practice from OO perspective to have stateless (or static) functions. I am yet
to have a good solution to this problem.

~~~
gmueckl
Objects shine when they encapsulate complex state in ways that disallow
illegal states to arise. The trouble starts once you try to add complex
transformation processes (e.g. business processes) to objects. When they work
on complex graphs of objects, you can't really put them into a single one of
them as none of them is the obvious place.

What I found to work best is to build an OO data model and a (not necessarily
functional) business logic layer on top. In pure OO languages like Java and C#
this layer takes on singleton characteristics, which is a clear sign that this
code should be simply procedural. The individual functions are often not very
interdependent and easy to work on and test.

------
khasan222
I enjoy using a mix of the two. I find that some things are better expressed
in OOP over FP, and will regularly mix them in the same code base. It lends
itself to very nice code that doesn't force you to do anything but make it
easier for someone who has never seen it to fix it.

------
visarga
The scenario where OO shines is game programming. Each object encapsulates a
game entity and it basically runs like a simulator, allowing for complex
interactions between objects. But software in general is not like a game and
OO doesn't help as much.

~~~
TeMPOraL
That about never happens. Partly because in no mainstream OO language a game
entity can "run like an [independent] simulator" \- you'd need green threads
for that. Partly because the industry has learned its lessons with trying to
shoehorn things into inheritance structure, resulting in god classes (see e.g.
original UnrealEngine's Actor class, or Pawn class). Partly because mainstream
OO is too weak to be useful (e.g. no multimethods). Partly because of cross-
cutting concerns - physics, "game logic", rendering, AI and networking - each
preferring slightly different view of things. Partly because performance - if
you structure things in exactly the wrong way according to OOP, you get better
cache use patterns. Partly because OO's "complex interaction between objects"
is complex in the wrong directions, and too simple in the right ones - hence
the current trend of composition-based "entity" modeling, when you encapsulate
behaviors in tiny tags and treat game objects as (mutable) arbitrary
collections of those tags.

I still struggle to find areas where full-blown classical, class-based OOP is
a good fit for the problem. Game development isn't such area.

~~~
bni
That inheritance structures is bad, leading to very highly coupled designs
that are hard to change, has been similar discovered by many in line-of-
business enterprise software.

Some claim (Like Gosling) that inheritance was never a major point of OOP and
that it has been overused.

What do you mean by "tag" here. Is a "tag" implemented as a class in C++? Do
you know of an example in real code that is open source, the the curious can
study?

~~~
TeMPOraL
> _What do you mean by "tag" here. Is a "tag" implemented as a class in C++?_

Google for "ECS", or "Entity-Component-System". It's a sorta-pattern for what
I described. I say sorta, because everyone has a slightly different idea of
how it's supposed to be implemented, but typically, entities are just dumb
containers for (instances of) components, the components store data & type
info, and systems mutate those components. So e.g. an asteroid in a game of
Asteroids would consist of e.g. "kinematics" component, and "sprite"
component, and a "collider" component. If you wanted that asteroid to drop a
powerup on death, you'd add "drops powerup" component to it, possibly at
runtime.

Components may or may not be implemented as classes, and the aggregation may
or may not be direct. In small games where I used this (in Common Lisp),
components were classes, and each entity object had a list of instances of
those components. This was a naïve approach, but got the job done. A more
performant implementation might look like this: each component is a struct,
and gets a dedicated array of all instances; each entity only has a) tags of
the types of components it consists of, and b) indexes into the "components"
array. This optimizes for data locality, especially for more isolated systems.
For instance, the physics system can now just loop over the array of
"kinematics" components, and mutate their state, without knowing anything
about actual game objects - and all the data necessary for updating physics
for all game objects are close together in memory, reducing cache misses and
allowing for other optimizations.

Note how this is a complete inversion of OOP - components are now storing just
state, all behaviours get segregated into systems that operate on those
components in batch mode, and entities exist only to tell you which instances
of components together form a game object.

I don't have any reference C++ implementation handy to show, but if you read
up on ECS, you're bound to find something.

------
brightball
IMO...

FP: Server Side

OOP: Client Side

~~~
hombre_fatal
After working with Elm, Clojure, React, and friends, I'd say FP also wins for
client/UI development.

For example, CoreData in iOS has to be my least favorite approach to a data
model yet. I spend a lot of time thinking "I wish this was Elm or React, it
would definitely clean this up" when building iOS apps in general. Definitely
feels like the state of the art has changed.

~~~
brightball
I could see that. I just see the stateful objects approach being more useful
on an interface.

