News for hackers. Stuff that matters.
Re:OOP Is Dead, Long Live OOP by westoncb on Thursday October 18, 2018 @05:28PM (#18252297)
For some reason 'arguments' against OOP seem to follow a common pattern. You have said many things against OOP, but you haven't actually presented an argument for why it's bad. I'll present each of your assertions here individually to clarify.

> OOP is prove a poor model for computation

> OO code is almost always significantly more complex and error prone than an equivalent computation written in a concurrent, functional, or structured paradigm.

These claims may or may not be true, but they aren't very useful at all if you don't provide a justification as well as merely asserting them.

> Recent trends in language design (see Rust, Go, Elixir) also seem to be abandoning OOP in favor of other models

There are a number of ways to account for these trends besides OOP being an intrinsically bad model. The most obvious one is that there are fashions in language design, and right now OOP is not fashionable. We already know that; it's not a strong argument against it. Ironically, many in the OOP opposition use the same argument to justify OOP every getting popular in the first place: "it was just fashionable."

> OOP provides competing ways of abstracting behavior that in Haskell we can model with type parameters and constraints.

> Objects are not truly encapsulated in the way Erlang processes are and a poor fit for SMP.

You have pointed out here that more classical OOP languages do things differently from Haskell and Erlang. This should be expected and is not an argument against those OOP languages. (Yes, you could say, "Erlang is better at concurrency" because of the way in which it's different—but my understanding is it's pretty well accepted that Erlang is sort of freak of nature here, so it's not a good argument against OOP generally.)

> Objects also pathologically hide data in attempt to manage mutability, making it impossible to reason about the memory layout of the program.

They do hide data, but the 'pathologically' is something you've added on your own. There is a design philosophy in which this data hiding plays an important, positive role. When you say, "making it impossible to reason about the memory layout of the program." —this sounds to me like missing the point of that design philosophy: the purpose (and oftentimes tradeoff) of higher-level languages is that you don't need to personally manage these details. I think it's largely an application-dependent thing: you many be writing code that requires that, but not all interesting software hinges on low-level performance tuning.

> OOP is a toolkit for building bad abstractions:

> ... abstractions that do not easily model computation

> ... that hide data

> ... and has tended to create overly complex solutions to problems that are often full of errors that a language focused more on type expressivity could catch at compile time.

Another collection of unjustified assertions, except the 'hide data' part which I accounted for earlier.

So across ~10 negative assertions about OOP you have 3 quasi-justifications: newer languages aren't using OOP as much, hiding data is bad, and Erlang is better for SMP.

Re:OOP Is Dead, Long Live OOP by loup-vaillant on Thursday October 18, 2018 @06:32PM (#18252783)
The pattern you talk about is mainly a product of not wanting to squeeze a whole essay into an HN comment.

I also suspect the problems with OOP are hard to communicate. I for one always had a problem with OOP, but I could never quite point it out. Sure, when faced with an OOP design, I could almost always find simplifications. But maybe I never saw the good designs? Maybe this was OOP done wrong?

I do have reasons to think OOP not the way (no sum types, cumbersome support for behavioural parameters, and above all an unreasonable encouragement of mutability), but then I have to justify why those points are important, and why they even apply to OOP (it's kind of a moving target). Overall, all I'm left with is a sense of uneasiness and distrust towards OOP.

Re:OOP Is Dead, Long Live OOP by westoncb on Thursday October 18, 2018 @06:48PM (#18252888)
> The pattern you talk about is mainly a product of not wanting to squeeze a whole essay into an HN comment.

That may very well apply to the GP's comment—but, my observation of the pattern is derived from a mix of mini-essay comments, and articles people are writing on Medium or their blogs or whatever, where the space constraints aren't so tight.

There are a couple things you'll regularly find: laughably bad straw-men (GP is free of these), overly vague statements that only survive scrutiny because of their vagueness (e.g. when the GP says OOP produces "abstractions that do not easily model computation"), and unjustified claims.

The net effect is something that sounds bad, but if looked at closely carries very little force.

I suspect the reasons for it are:

1) Actually evaluating a language paradigm is more difficult than these folks suspect. Their view matches their experience and they assume their experience is more global than it really is. Additionally, we don't have a mature theoretical framework for making the comparisons.

2) People are arguing for personal reasons. They have committed themselves to some paradigm and they want to feel secure in their justification for doing so.

Re:OOP Is Dead, Long Live OOP by Twisol on Thursday October 18, 2018 @08:36PM (#18253507)
> Additionally, we don't have a mature theoretical framework for making the comparisons.

This is really the problem. As much as I have strong opinions and beliefs about how to architect code, every argument I come up with boils down to some flavor of "I like it better this way". Which is true -- I do like it better this way -- but hardly actionable, and it doesn't get at the essence of why I like it better.

The problem with making everything an object -- or more precisely, having lots of mutable objects in an object space with a complex dependency graph -- is that it becomes very hard to model both how the program state changes over time and what causes the program state to change in the first place. I think the prevailing OOP fashion is to cut objects and methods apart into ridiculously small pieces, which takes encapsulation and loose coupling to an extreme. This gives rise to the popular quip, "In an OOP program, everything happens somewhere else." I can't think straight in this kind of setting.

I believe that mutable state should be both minimized and aggregated. As much as is humanly possible, immutable values should be used to mediate interactions between units of code (be those functional or OO units), and mutation should occur at shallow points in the call stack. Objects can work well for encapsulating this mutable state, but within the scope of an object, mutation should be minimized and functional styles preferred.

Using a functional style doesn't mean giving up on loose coupling or implementation hiding. Rust, Haskell, and plenty of other languages support these same concepts in the form of parametric polymorphism, e.g. traits or typeclasses. It does mean giving up on the idea that you can mutate state whenever it's convenient. Instead, you have to return a representation of the action you'd like to take, and let the imperative shell perform that action.

Speaking of imperative shells and functional cores, Gary Bernhardt's talk called "Boundaries" is an excellent overview of this kind of architecture [1]. There was also a thread here on HN about similar principles [2].

[1] https://www.destroyallsoftware.com/talks/boundaries

[2] https://news.ycombinator.com/item?id=18043058

Re:OOP Is Dead, Long Live OOP by westoncb on Thursday October 18, 2018 @09:12PM (#18253638)
That makes a lot of sense to me. Looking forward to checking out "Boundaries".

Btw, one other idea I've had on the subject is that the problems with mutable state can be mitigated if we were able to more easily see/comprehend the state as it's being modified by a program; without that capability the only recourse we're left with is our imagination, which of course is woefully inadequate for the task. You can see more concretely what I'm talking about in my project here (video): http://symbolflux.com/projects/avd

From what I've seen, structuring a program to not modify state is almost always more difficult than the alternative[0]. There are certain problems where this difficulty is justified (because of, e.g., reliability demands); but I think most problems in programming are not those, and if we could just mitigate the error-proneness of state mutation, that may leave us at a good middle ground.

[0] The exception is when you're in a problem domain that can naturally be dealt with via pure functions, where you're essentially just mapping data in one format to another (i.e. no complex interaction aspects).

Re:OOP Is Dead, Long Live OOP by Twisol on Thursday October 18, 2018 @09:47PM (#18253763)
Oh, that's very cool! I had a similar idea years ago, but I didn't have the technical chops to pursue it at the time, and I ended up losing interest. I think this would actually be even more useful in the kind of architecture I'm describing, since the accumulated state has a richer structure, and many of the smaller bits of state that would be separate objects are put into a larger context.

> From what I've seen, structuring a program to not modify state is almost always more difficult than the alternative

You're not wrong! I don't think we should get rid of mutable state, but I do think we should be much more cognizant of how we use it. Mutation is one of the most powerful tools in our toolbox.

I've found that keeping a separation between "computing a new value" and "modifying state" has a clarifying effect on code: you can more easily test it, more easily understand how to use it, and also more easily reuse it. My personal experience is that I can more easily reason locally about code in this style -- I don't need to mentally keep track of a huge list of concepts. (I recall another quip, about asking for a monkey and getting the whole jungle.)

There is a large web app at my workplace that is written in this style, and it is one of the most pleasant codebases I've ever been dropped into.

Re:OOP Is Dead, Long Live OOP by westoncb on Thursday October 18, 2018 @10:06PM (#18253814)
Interestingly, I think I built that project with an architecture somewhat reminiscent of the 'boundaries' concept (still just surmising at this point). It's a super simple framework with two types of things 'Domains' and 'Converters'. Domains are somewhat similar to a package... but with the boundaries actually enforced, so that you have to explicitly push or pull data through Converters to other Domains; Converters should just transform the format from one Domain to that of another (they are queue-based; also sometimes no translation is necessary).

I'll quote from the readme:

> This Domain/Converter framework is a way of being explicit about where the boundaries in your code are for a section using one ‘vocabulary,’ as well as a way of sequestering the translation activities that sit at the interface of two such demarcated regions.

Inside each Domain I imagine something like an algebra... a set of core data structures and operations on them.

But yeah, I have very frequently thought about visualizing its behavior while working on that visualizer :D

Is your research related to programming languages?

Also I'm going to have to think about "computing a new value" vs. "modifying state" —not sure I quite get it...

Re:OOP Is Dead, Long Live OOP by Twisol on Thursday October 18, 2018 @11:04PM (#18253982)
> Is your research related to programming languages?

Yep: I just finished a Master's degree with a focus on programming language semantics and analysis. I'm interested in all kinds of static analyses and type systems -- preferably things we as humans can deduce from the source without having to run a separate analysis tool.

> Also I'm going to have to think about "computing a new value" vs. "modifying state" —not sure I quite get it...

It's kind of a subtle distinction. A value doesn't need to have any particular locus of existence; semantically, we only care about its information content, not where it exists in memory. As a corollary, for anyone to use that value, we have to explicitly pass it onward.

On the other hand, mutation is all about a locus of existence, since ostensibly someone else will be looking at the slot you're mutating, and they don't need to be told that you changed something in order to use the updated value. (Which is the root of the problem, quite frankly!)

Re:OOP Is Dead, Long Live OOP by jbergens on Monday October 22, 2018 @11:38AM (#18275533)
You do realize that a lot of people could have "a sense of uneasiness and distrust towards FP"?
Re:OOP Is Dead, Long Live OOP by dorgo on Thursday October 18, 2018 @06:24PM (#18252706)
I upvoted you. Why OOP is bad: In 8 years professional (for money) programming experience i had zero use cases for OOP. Every time I tried to use OOP it backfired and I abandoned it. Maybe I just never really understood OOP or maybe I already use OOP all the time without calling it OOP.
Re:OOP Is Dead, Long Live OOP by westoncb on Thursday October 18, 2018 @06:51PM (#18252912)
My main conclusion on the subject is that it really depends on what domain you're coding in.

There are plenty of applications I would never use OOP for. I use Elixir for my backend work. I tend to use OOP for interactive simulation/game sorts of applications.

Re:OOP Is Dead, Long Live OOP by da02 on Thursday October 18, 2018 @06:49PM (#18252897)
What were the types of problems and systems you have worked on? Which languages and programming styles (eg functional) did you end up using to solve them?
Re:OOP Is Dead, Long Live OOP by dorgo on Monday October 22, 2018 @12:16PM (#18275940)
Mostly math, optimization and data transformations. Since java8 I use functional style (map, filter, monads..). A lot of business logic is also in sql queries.
Re:OOP Is Dead, Long Live OOP by tcbawo on Thursday October 18, 2018 @06:47PM (#18252885)
Bad code is bad code, no matter which language. Languages won't save you from poor design choices.

OOP is popular in large enterprise systems because it (purports to) promote encapsulation and abstraction, which allows many teams/people to interact. Organizations may like OOP because it helps reinforce their drive for independence and fiefdom (see Conway's law https://en.m.wikipedia.org/wiki/Conway%27s_law). Whether it is the best paradigm, the most practical, 'just good enough', the wrong choice is anecdotal. Also, in experience many companies would rather fail conventionally than succeed unconventionally.

Re:OOP Is Dead, Long Live OOP by antidesitter on Thursday October 18, 2018 @07:18PM (#18253170)
> Bad code is bad code, no matter which language.

It’s easier to write bad code in some languages than others.

> Languages won't save you from poor design choices.

They do. They save you from entire classes of bugs, and make it less easy to shoot yourself in the foot.

Do we need to have this conversation every time we talk about language design? Do you think the tool you use to achieve a task doesn’t matter?

Re:OOP Is Dead, Long Live OOP by pka on Thursday October 18, 2018 @07:39PM (#18253271)
> Do you think the tool you use to achieve a task doesn’t matter?

I think many people do think that, with a twist; specifically, when presented with a higher level language they'd argue "it's just a tool", while a lower level language would be "the wrong tool for the job".

I.e, for a hypothetical Ruby programmer, Haskell = "just a tool", C = "the wrong tool".

Re:OOP Is Dead, Long Live OOP by The_rationalist on Thursday October 18, 2018 @06:40PM (#18252843)
Such a precise epistemic analysis should be incentivised on hacker news. Thank you.
Re:OOP Is Dead, Long Live OOP by didibus on Friday October 19, 2018 @01:34PM (#18258389)
You're right, people go with a all or nothing. Yet no OO is the same in two language. This is problematic both ways, I have a lot of people ask me how the hell do you model entities in FP? Because they equate entity modeling with OO.

From my view, here are the concrete issues I have with some of the OO flavors out there.

1) It encourages shared mutable state.

OO creates a new layer of shared state, the object fields. In its very essence, the idea is to have methods which accesses and modifies the object state through direct access of its fields. Thus each field is mutated by the object methods which directly access them. You need to look at the method code to know what state they read and write too. You can not make the fields immutable, because that renders non read only methods useless. You must thus coordinate access of the fields between the methods using explicit locks. Over time, in practice, it also creates massive classes with too many fields and too many methods that only make use of a subset of them, degenerating into even more of a globally shared state structure.

2) It makes dependency injection trickier and thus discourages its use.

Once again, the idea of methods to have direct access to fields is the cause of this, it means that methods don't have their dependencies injected, instead they go find them themselves, through direct access. Testing becomes hard, configuration is pushed down the stack inside the methods and reuse is made harder.

3) It makes inversion of control trickier and thus discourages its use.

Because code can only be passed around wrapped inside an object, and objects are heavy constructs requiring a lot of verbosity to create: a new file is required, a class must be defined, an instance must be created, etc. It means that in OOP it is rare to see code being injected from caller to callee. Instead, conditionals creep inside the callee, and configuration parameters are passed it.

4) It handles stateless code poorly, and thus discourages its use.

Code that requires no state over time, aka stateless operations must be wrapped inside a class for no good reason. OO provides nothing to such code. OO is designed for statefull code. A class with no fields, it's not useful. Why do I need one if all my code is stateless? So people start using them like namespaces.

5) Inheritance is too easy to mess up.

Inheritance as a mechanism for code reuse appeared to be pretty smart at first. The problem is it turned out its pretty hard to do right. You soon find yourself with hidden state from parents and confusing override hierarchies, which forced us into single inehritance chains, which limited code reuse, etc. Bottom line, it just created ton of hidden coupling.

6) Objects are not extendable from the outside.

You can't add functionality or state to an object from the outside. You need to modify the source code of the class directly to do so, or rely on defining new subclasses through inheritance. This minimizes code reuse, and encourages object code to grow ever so bigger as more and more features are added.

That's all I can think of for now. Now, some flavours of OOP work differently in that some or all of these problems might not exist or have solutions to them. Which is why I agree with you, it's best to know the actual problems so you can spot them. Just saying something is OOP doesn't imply all of them will exist.

A lot of languages allow stateless subroutines to exist on their own and offer a seperate namespace system not conflated with the OO layer. Trait or mixin like systems enable open extension. Inehritance can support multiple parents, or composition is used in its place. Some allow subroutines to also be passed around, not needing to be wrapped in an object. Dependency injection frameworks were added to simplify and encourage its use. Value objects allow a bit more support for immutability. Etc.

FP doesn't suffer from these issues though. That said, it has others in its place, and different FP languages have also found different solution to them. That said, in my experience with many languages that were more OO oriented and ones that were more FP oriented, I found FP overall had less issues and had found cleaner solutions to its limitations.