Live data from Hacker News

Case against OOP is understated, not overstated (2020)

boxbase.org

521–530 of 557 posts

Re: Case against OOP is understated, not overstated (2020)

#521
post #331

Earlier quoted context omitted.

> Ouch, not even unit tests to catch regressions? I also don't like unit tests and very rarely unit test. I think they provide a false sense of security and were invented by corporate software shops to better quantify "units of work" (oh, how many times I've had unit tests assigned to me in tickets!). If you can't formally prove something doesn't break (in your head or with pen & paper or via pseudocode), your code i…

I'd rather have an existing suite of tests that I can use to verify that when working with other people's code. And more importantly, if JIRAISSUE-15295 is reproducible, then a unit test that a) reproduces it and b) verifies that the bug no longer occurs, is invaluable to prevent someone bringing JIRAISSUE-15295 back from the dead. Of course, if the unit test is too tightly coupled, and has too many insights into cod…

A rapidly changing codebase is where unit tests are least useful though. Most of the changes are going to be because of new requirements which just means the test has to be updated. It's just busywork at that point.

If you have discovered a way to write unit tests that can tell the difference between a regression and an enhancement, please let me know.

Re: Case against OOP is understated, not overstated (2020)

#522
post #495
post #296

Earlier quoted context omitted.

The key that Joe was trying to make I think was that the forest and the gorilla have to be explicit: State2 = update(State1, ArgX), State3 = update(State2, ArgY), As opposed to, say: obj.update(arg_x); obj.update(arg_y); obj holds a gorilla and the jungle, and it may be hard to know how update method works because there is a complicated diamond shaped class hierarchy, and then a thread may concurrently modify parts o…

The problem is that setters are usually void functions. Instead they could return the new state/object State2 = obj.update(argx) State3 = State2.update(argy) But if you do this people will argue this is inefficient, because you are copying a lot of objects.

> But if you do this people will argue this is inefficient, because you are copying a lot of objects.

Syntactically it is annoying to some extent. But, just like the most important data gets saved to a database with lots of "ceremony" involved -- separate protocols, transactions, SQL statements, etc, because it's pretty important to track updates carefully. Here ,it's a bit like that, but on a smaller scale as in memory program state is also important, and has to be tracked explicitly, and for that some "ceremony" is acceptable.

Implementation-wise, because of immutability, there is copying but it is often not 100% duplication. The updated version and the previous one behind the scenes (in heap) might share a lot of common structures. For example, if we have a 1000 element list L, and we updated it with a new element at the front L1 = [H | L], then L1 and is not a complete copy of L, but instead is just element H and a pointed to the shared tail L. For dictionary data structures (maps) something similar happens but there it's a O(log n) order of updates with everything else being shared. Definitely not as efficient as in-place updates in say C++ or Java but that's a price I'd happily pay.

Re: Case against OOP is understated, not overstated (2020)

#523
post #14

In my mind, state is the real enemy impacting: comprehension, brittleness towards making changes, and the surface area exposed to potential bugs. OOP as frequently implemented, while claiming to encapsulate state, ends up creating so much more. In accordance with this view, I think project architecture should be approached with an emphasis around how much state is necessary for it to run. This is why simulations like…

It's definitely that, but to be fair the problem is caused by classg-based programming, not OOP itself.

Put state on an object only if there is a hard requirement for it. The occurrence is incredibly rare, state is mostly introduced to save re-typing method arguments...

Re: Case against OOP is understated, not overstated (2020)

#524
post #14

In my mind, state is the real enemy impacting: comprehension, brittleness towards making changes, and the surface area exposed to potential bugs. OOP as frequently implemented, while claiming to encapsulate state, ends up creating so much more. In accordance with this view, I think project architecture should be approached with an emphasis around how much state is necessary for it to run. This is why simulations like…

If only we could completely eliminate state! Thankfully, I am working on a plan for this. It should take around 10^106 years... give or take. The serious comment here is that the real world imposes a minimum floor on the amount of mutable state that you have to model. Databases are giant piles of mutable state. Maybe we should start talking about "essential state" and "accidental state" the way we talk about complexi…

I like the term accidental state. This is also the type of state you see in a lot of OOP code, as referred to by parent.

Beginners want to keep functions short and the way to do that is to chunk up a bigger method into several smaller, then realize oops, that you needed that variable in both functions. Store it into “this” and now instead of one decoupled function you have two coupled functions.

Contrived example written on phone but code like below is extremely common, especially from Java coders who have been mislead to make classes for everything and haven’t learned the static keyword yet. Here obviously the self.stuff is the accidental state creating coupling between the functions, that now carefully have to be called in correct order and any of their mutations to self can impact the other.

    class Worker:
    def init()
        self.setup()
        self.foo()
        self.bar()
    def foo()
        self.stuff = fluff
    def bar():   
        do_work(self.stuff)
Rather than just do_work(bar(foo(fluff))).

Re: Case against OOP is understated, not overstated (2020)

#525
post #240

Earlier quoted context omitted.

>solutions to imaginary problems This is a fundamental misunderstanding of what patterns are. The GOF book is used to this though. A design pattern is someting that will naturally crop up if you adhere to certain design principles. If you follow a principle of separating instantiation logic from other logic then you will start to see factories. If you combine multiple complex parts of your code into simpler ones then…

That exact sentiment exists deep in my comment history here: I think I used the word "blueprint" if you care enough to fact check that. That is why I said I don't hold issue with the content of the book. My issue is that the book is useful. It helps solve the artificial complexity introduced by OOP.

Agreed that it's never good to have new social problems created by whatever you did to solve the previous problem. But that's the nature of doing stuff.

I own a car. Great, now I have maintenance concerns. But it's still a net positive, which is why we do it.

If oop creates more work than the value of brings, then we should scrap it. But I've seen some pretty bad procedural code, so I don't think that's a given.

Re: Case against OOP is understated, not overstated (2020)

#527

Earlier quoted context omitted.

> so far I don't know what Go or rust programmers do that is any different Rust doesn't have object inheritance. So Car and Truck can't inherit from a class named Vehicle. However its Traits have inheritance, so for Car and Truck you could write implementations of a trait named Vehicle or a trait MotorTransport which inherits from Vehicle (and so you'd need to implement Vehicle too in this case as MotorTransport reli…

If traits map to interfaces and you have concrete implementations of those which can be composed into Car then rust is still just subtractive and eliminating inheritance. There's nothing you can do in rust then that you couldn't fundamentally do in OO languages by avoiding inheritance. Which gets to the point of if we should be talking about avoiding inheritance specifically? Because "OO" is somewhat ill-defined.

Inheritance isn't gone from Rust it just only exists for Traits (akin to interfaces) as I explained.

For example std::cmp::Eq inherits from std::cmp::PartialEq - if there's an Equivalence relation between things of this type then necessarily there is also a Partial Equivalence relation between some of those things (specifically: all of them) so you implement std::cmp::PartialEq and then just say actually it's also Eq (ie this equivalence applies to the whole type).

If you make some types which you implement std::cmp::Eq for, and all I need is std::cmp::PartialEq, I can use your types, because of the inheritance. But the fact your types have std::cmp::Eq (and thus also std::cmp::PartialEq) does not prevent them from being quite different in every other respect to other types, nothing about the types themselves is inherited.

So this means thinking about inheritance in a different way but it doesn't mean the concept is gone from the language. A typical toy Rust type might implement half a dozen or more Traits, some of them inherited from others and some not, "eagerly" implementing common Traits is encouraged.

As to just not using subtype inheritance in languages which have that, you're likely to immediately run into an existential crisis when the language - not unreasonably - depends upon this feature in its own design. In Java for example you can't go anywhere without tripping over Object, the supertype of all user-defined types. Java expects you to use inheritance so avoiding it comes with needless penalties.

Re: Case against OOP is understated, not overstated (2020)

#528
post #341

Earlier quoted context omitted.

But then you're mixing up the state of the system with the shape you want to draw. If you were now working with 2 pens, you'd have to rewrite your shape from scratch too, not just your rendering, to speed up the output. Better to separate the shape data, which is immutable (and basically declarative), and the rendering method, which does need to know about the previous work which was already completed and what it is…

Maybe. There is a reason gcode exists. Sometimes you really are controlling a single pen.

Gcode exists because hardware abstractions are even more leaky than software extractions. When pixels go onto your screen, you don't assume to know better than the guy who wrote the driver for the graphics card how they should get there. When plastic gets deposited on a 3D printer the way in which it is deposited actually affects the properties of the resulting object. Same for a CNC lathe or milling machine, although to a lesser degree.

There are of course also historical reasons, when it would be a central mainframe that would generate the gcode, and then it could be executed many times by cheaper computers attached to the machines. There was even a point where a lot of gcode was written, or at least edited, by hand. In these modern days of compute excess, gcode probably wouldn't have developed to the extent it did, and we'd be distributing STLs with some metadata around tolerances, materials and primary stress directions and the machines would figure it out themselves. The equivalent of gcode would just be used as a communication protocol between the interface and the motor controllers.

Re: Case against OOP is understated, not overstated (2020)

#529

Earlier quoted context omitted.

I've provided a very specific definition of what good OOP requires so it's not fair to suggest that my argument is pointing to some vague characteristics or is evasive. I can look at any project's code and assign it a score in terms of cohesion and coupling of the classes/modules/components. Other people who are experienced with OOP can look at the same code and they will come up with a similar score.

The point of "No True Scotsman" is that you have your own definition of high quality OOP, which is not universal. Maybe yours is the right way of doing things, IDK. But I think others would probably prefer different definitions. For a lot of people, this variation in opinion of how OOP should be used can lead toward a conclusion that OOP in and of itself is a confusing concept and difficult to get "right". Some peopl…

FP solves some issues but simultaneously introduces a new set of issues which creates new 'No true Scotsman' debates around ways to address those new issues... It's like the joke that there were too many competing standards and so somebody decided to invent a new standard to make all the other standards redundant... The net result of this is that we end up with n + 1 competing standards.

IMO, the biggest problem I often see with FP code bases is poor separations of concerns which leads to spaghetti code which is hard to read and maintain. When some state is not co-located the logic which is supposed to be operating on it, you're already throwing high cohesion out the window... And when you do that, it makes is harder to separate the responsibilities of different components because there is no clear ownership relationship between the logic and various bits of state... With FP state can end up being mutated all over the place and it's hard to know who did what.

Re: Case against OOP is understated, not overstated (2020)

#530
post #333

My issue with OOP is: Design Patterns: Elements of Reusable Object-Oriented Software. I don't take issue with the authors, with their insights, or anything related to the content of the book. It's that the book exists at all: it's a book filled with solutions to imaginary problems. When using a procedural language the first thing you do is start implementing a solution. When using OOP, you first have to solve the ima…

> it's a book filled with solutions to imaginary problems. Have you even read it? I'd say it has more deep real world examples than almost any other SW engineering book I've read.

IME it happens often that these categories overlap, when software gets entangled in incidental compexity.
Post reply on HN