13 ms·
This doesn't speak to what I consider some of the most dangerous parts of OOP, which include the assumptions that tightly binding data and code is helpful, and
by lgessler 6y ago
This doesn't speak to what I consider some of the most dangerous parts of OOP, which include the assumptions that tightly binding data and code is helpful, and that statefulness is fine to freely sprinkle throughout your program.
- mimixco 6y agoAgreed. The biggest problem with OOPS lies in its original premise: that code and data should be the same thing! This is grevious error, IMHO, that leads to nothing but problems: endless boilerplate code, lots of "interfaces" that essentially do nothing (that couldn't be done directly with said data), and debugging nightmares. I much prefer to work with systems where code and data are separate and never the twain shall meet.
- michaelcampbell 6y agoOOPs original premise is homoiconicity? Really?
- throwaway894345 6y agoI'm not sure "code is data" necessarily means "homoiconicity", but it could mean "first-class functions" (or at least that's what I think of when I hear the phrase; maybe I'm wrong); however, I'm not familiar with anyone who argues that these are fundamental OOP features (perhaps Smalltalk had first class functions, but for a very long time the most popular flavors of OOP did not).
- _old_dude_ 6y agoyes, Homoiconicity is about having the same syntax for both structure and data. First class functions means there is no conversion, again at syntax level, between a function and a value. Mainstream languages all have a way to see code as data, it's function pointer in C, delegate and lambda in C# and first class functions in Python or JavaScript.
- throwaway894345 6y agoI understand the differences between homoiconicity and first class functions and even that modern languages often support them, but that support is typically an artifact of a functional (and not OOP) influence with the exception of function pointers in C whose origins im not sure about except that it doesn’t come from OOP. The point is that despite the confusion about “what does OOP mean, really”, there are no prominent arguments that suggest that homoiconicity or first class functions are defining characteristics.
- TOGoS 6y agoI'm pretty sure what was meant by 'code is data' is that a lot of OOP training encourages you to "bundle the code and the data in an object". People take that to heart, and...bundle code and data into objects. Whether or not those functions and that data really belong in the same object is an afterthought, if considered at all.
- winstonewert 6y agoI presume "code and data are really the same thing" is intended to refer to way that OOP binds code and data together.
- TheOtherHobbes 6y agoI'm not sure anyone really knows what OOP's original premise is. There are various legends, and they mostly conflict with each other.
- goatlover 6y agoIt was whatever Simula and then Smalltalk's goals were.
- MasonOfSpoons 6y agoIt feels a bit like a rhetorical device to discuss OOP in any context other than the version of Java that's currently in widest usage.
- prostodata 6y agoI think both conceptions are important. On one hand, some code is naturally related to certain data and hence we should accordingly design the program. On the other hand, complex and distributed programs have code which must be dynamically bound to different data. OOP focuses on only one aspect but does it very well. How to adequately support the opposite aspect is not clear. At least there are many ways how this can be done.
- tehjoker 6y agoThis reads like a rejection of von Neumann architectures. I don't think this is quite right. https://en.wikipedia.org/wiki/Harvard_architecture https://en.wikipedia.org/wiki/Harvard_architecture
- Barrin92 6y agoThe entire point of OOP is to encapsulate state within objects and to have objects communicate through messages, that is to say to 'program without a center'. Global or interconnected state is if anything a violation of OO principles. In proper OO state is hidden and insofar as it causes issues it does so in a way that is handled by the object rather than the program overall. Also treating code as data isn't a feature of OO, it's an old idea starting with Lisp and metaprogramming I suppose and it's present in functional languages as well. It basically just means that the language itself is a first class data type within the language.
- thom 6y agoI'm not sure you're responding directly to the point the parent made. Even if state is encapsulated, objects are mutable. Many people believe reasoning about programs in which lots of mutable objects coordinate is harder than some of the alternatives (e.g. pure functions, immutable data etc).
- Barrin92 6y agodepends on the domain. In anything that has say, a very structured 'data flow' in one direction, like in finance or accounting or versioning immutability works well as a concept. In other domains like say, game development it feels forced and out of place. If actual usage of paradigms is an indication rather than belief then it doesn't look like functional programming really is intuitive, it tends to be very useful in certain niches.
- jeffffff 6y agogame development seems to be moving away from OOP and towards entity component systems and data oriented design. i've heard arguments that OOP's killer application is desktop guis, which i could buy, but those become less and less relevant daily. (caveat: my experience is entirely in back end and low level software and anything i say about front end or ui development should be highly suspect)
- 6y ago
- tgflynn 6y agoSo what is your preferred solution for programs that intrinsically need to deal with a lot of state ? Global variables ? I used to see that type of approach in some complex Fortran simulation programs where every function referenced a huge common block. I really don't think that leads to easier to manage solutions.
- lgessler 6y agoEssentially, yes, but in a way that sounds more careful than what you saw in that Fortran program. If it absolutely has to be stateful, like with a db connection for example, then there's no getting around that, but what you can do is (1) not bury it in a bunch of stateful objects that didn't have to be stateful, which can cause cascading errors when either one of them misbehaves and also makes it difficult to isolate any one stateful component for testing or re-use, and (2) use special wrappers around it that will make the statefulness easier to deal with. (For examples of (2), see Clojure atoms and the Clojure library mount: https://github.com/tolitius/mount https://github.com/tolitius/mount )
- nostrademons 6y agoMore concretely, for every bit of state in the program I'm interested in two big questions, each with two subquestions: 1.) Who can access this state, and which of those accesses allow writes vs. which are read-only? 2.) What is the lifetime of that state, and what portion of that lifetime is mutable vs. read-only? Java-style OOP can control #1, but makes no distinction between reads & writes. C++ const correctness and the val/var distinction in Kotlin/Ocaml/ES6/etc. can make that distinction, but only C++ forces you to think in terms of lifetimes. Rust borrowing probably comes closest to answering all 4. I'm still waiting for a language feature that lets you say "This field of this struct is only mutable while this module is running, and once it completes it will never be changed", though. A lot of data structures are initialized in passes and then passed along as constant data: you construct the basic object structure with nothing but a few IDs, then you compute extra fields as necessary, then you're done and the object is never written to again. If you could keep the fields mutable during initialization (which may be a longer process than just constructor calls) and then seal them off once that's complete, you eliminate a whole class of bugs where a read-only client decides to mutate a field that should only be mutated from designated initializer.
- dionian 6y agoI don't see why the Law of Demeter cannot be adhered to in OOP. I do it every day.
- juniper_strong 6y agoMe too. I also violate it every day. Just this day I used System.out.println, for example.
- hderms 6y agoYeah imo law of demeter shouldn't apply for namespace organization of static, globally relevant functionality. The amount of dots in that case is irrelevant to me because it doesn't really imply changing context in the same way. more like drilling down in a phonebook than calling your neighbor and telling them to call Bob for you
- garethrowlands 6y agoA typical microservice tightly binds code and data, and it communicates via messages. So when a microservice is useful, it's an example of binding data and code being useful. Though when a microservice mutates something, it's typically persisted in a database or similar (and, you'd hope, not sprinkled throughout its code). But, yeah, a bunch of communicating microservices are a lot like communicating objects - for better or worse. Same with Erlang-style actors.
- Guvante 6y agoTightly binding code and data works as long as a useful abstraction is created. Keeping the structure of a person and how to interact with that person together is a fine paradigm. Usually the issue with OO is that the abstraction isn't there and it is just half the functions to interact with the person and/or there is an expectation that certain fields are manipulated by calling code instead. Microservices require every action you take be a message and enforce that messages are the only way to communicate which makes it easier to avoid indirect (and thus hard to track) dependencies. For instance if the person's age is updated when you calculate their hours worked, in OO that might be hidden from view but in message passing you would at least see that a new person object came back that replaced the one you had. None of this stops you from doing evil unobvious things with your hidden information (updating everyones age whenever anyone gets overtime) but it is usually harder to accidentally do that. To be clear while I like microservices I think they have many problems just like OO. For instances caching becomes super complicated and there is a real cost of going over the network for everything you do. The tradeoffs are just a different set of tradeoffs.
- addicted44 6y agoEventually data has to be bound to code. Otherwise you'd have apps where you either have functional code running and other apps that are displays of raw data. The engineering question is what layer is it appropriate to do so. Under traditional OOP (your pre functional forward Java/C#/OOP C++), the answer was that code is almost always bound to data. Haskell style functional programming binds code to data at the last moment possible. The best answer is likely somewhere in the middle, depending on your application and usage. I find, for example, that UI controls work well with OOP, but most other stuff I tend to default to functional style programming.
- jayd16 6y agoObjects are essentially structs with namespaces and methods with implicit this params. If you can't have state then you throw structs out too. If you're going this far, you're essentially throwing away typing as well. Are you actually just opposed to private fields?
- JackMorgan 6y agoSeparating functions and state has nothing to do with the type system. Otherwise how could you put types on the parameters to static functions? Check out F#, OCaml, or Haskell for plenty of examples of how you can have strong static types without mixing state and functions in a scope together.
- jayd16 6y ago>Otherwise how could you put types on the parameters to static functions? Object methods are just sugar for this, so whats the difference to you? When you say "separate" do you mean you just want it in different files? Different curly braces? Different namespaces? Is it inheritance and virtual functions?
- winstonewert 6y agoI think the key differentiater is that OOP (in some versions) wants to hide the data and not just implementation details. For example, a hash map data structure hides the details of how the hash map is implemented, but it doesn't hide the data. In some versions of OO, the object should not only hide the implementation details but also the data. Any function that needs that data MUST be a method of that object. That's where things go haywire.
- jayd16 6y agoHash maps hide lots of internal state though. I think the difference is hash maps have a clear, battle tested API and its obvious what should be promised by the interface and what should be internal implementation. Devs aren't clairvoyant so when we're making a new type we might sometimes get this wrong. But this is a fundamental issue with type systems in general and not OOP, is it not?
- agumonkey 6y agoOOP was a (too) big response after an era of semi-wildness.. I believe nobody mastered the whole cake and it was rarely applied fully. Most of it is simply mental logistics convenience (most related methods are one dot away) and of syntax. car_start(&car) is noisy; car.start() a tiny bit less so.
- lkrubner 6y agoI was about to write exactly this. In 2014 I wrote an essay that has been discussed here on Hacker News several times [1]. When I re-read it now, parts of it seem very subtle, parts of it seem to be matters of opinion, but what jumps out is how really dangerous it is to tightly bind data with behavior, especially because this then leads to the problem of initiation (which I devote a lot of time to talking about in the essay). [1] Object Oriented Programming Is An Expensive Disaster Which Must End http://www.smashcompany.com/technology/object-oriented-programming-is-an-expensive-disaster-which-must-end http://www.smashcompany.com/technology/object-oriented-progr...
- dzonga 6y agoI enjoy reading your takes, e.g the Case against Docker. It's good to have sane minds still around in the industry.
- cellularmitosis 6y agoThank you for this. I am so tired of this exchange: A: OOP isn't so bad, actually. B: But what about all these terrible messes? A: Oh, that's not a problem with OOP, they're just doing it wrong. I'm sorry, but when 80% of the industry (and 100% of new grads) are "just doing it wrong", it isn't helpful to no-true-scotsman the critics. The only way I see out of this endless game of semantics is for someone to canonize the tightrope of practices which are "OOP done right", and then give it a different name.
- Ace17 6y ago> The only way I see out of this endless game of semantics is for someone to canonize the tightrope of practices which are "OOP done right", and then give it a different name. Our industry has a naming problem when it comes to practices, especially when the practice is subtle and complex but its name is simple and trendy (maybe it's not specific to software, IDK). Dev teams are mislead into thinking that they can deduce the practice from the name (and maybe a 1h training with a self-proclaimed expert in the practice). This is how we got aberrations like: - "TDD" devs writing their tests afterwards - "Agile" teams with 6-month release period - "SCRUM" masters that act as classic managers New names referring to "good/right/trendy" practices are doomed to get claimed by "80% of the industry", in a way that doesn't respect the original practice. Alan Kay recently said that maybe he should have called it "Message-oriented programming" ( instead of OOP ). While not having "object" mostly eliminate the risk of the "one-class-per-real-world-object" antipattern, we might as well had ended up with other anti-patterns based on wrong interpretation of what constitutes a "message". This is why I think changing the name of any practice is inefficient. The misunderstanding isn't accidental, it's systemic (Moreover, renaming a practice would probably create even more confusion) Names that refer to common mistakes wouldn't get claimed ; no team is going to proudly explain to their customer that they're using the "big ball of mud" antipattern, the "scrum but" team organisation, or the "debug later" dev practice. So maybe we should better canonize OOP antipatterns?
- zozbot234 6y ago"Tightly binding data and code" allows you to maintain complex invariants via code, and to abstract away from the specifics of any single implementation. Statefulness per se is quite manageable; what's not manageable is shared, mutable state that isn't isolated to a single, modular, highly cohesive "unit" of code that can be understood in its totality. The real issues with OOP have to do precisely with things that break these principles, viz. implementation inheritance.
- keithasaurus 6y ago> "Tightly binding data and code" allows you to maintain complex invariants via code Not sure I understand what this means. Could you give an example?
- fdupress 6y agoA good example would be a complex state machine, where you rely on the fact that the state is only modified by the state machine code to make sure it remains well-formed, and the control-flow itself heavily relies on the state being well-formed (think of a TLS session, for example). But then again, what you want there is modularity, which OOP provides, but is also well-supported in other paradigms.
- Gibbon1 6y agoYeah but you don't need OOP to do that. See opaque pointer's in a number of non OOP languages.
- augustk 6y agoThese are called Abstract Data Types.
- garethjrowlands 6y agoThis paper describes the differences between objects and ADTs, https://www.cs.utexas.edu/users/wcook/papers/OOPvsADT/CookOOPvsADT90.pdf https://www.cs.utexas.edu/users/wcook/papers/OOPvsADT/CookOO...
- ysavir 6y agoCan you expand on your opinion? Do you consider all instances of binding data and code dangerous?
- AnimalMuppet 6y agoIf your program has statefulness (worse, has to have statefulness), then it seems better to control where it is (encapsulate it), rather than letting it run all over the place in the program. And, if you have code that is for working on a particular kind of data, why is binding it to the data problematic?
- wwright 6y agoI think "tightly binding data and behavior" is a less-ambiguous way of phrasing the pitfall. Obviously code goes great with data; no one wants to work with strings as raw buffers all the time!
- baryphonic 6y ago> This doesn't speak to what I consider some of the most dangerous parts of OOP, which include the assumptions that tightly binding data and code is helpful,[...] I don't agree at all. It's much more dangerous to separate data representation from the code that manipulates the data. I can't tell you how many bugs I've found and had to fix in large codebases (most of which are procedural-code-in-"OOP"-languages) that result from some horrific evolving arrays-and-records structure not recording intent and validity constraints properly (since that would require, you know, code). Instead, each interaction with the Holy Data Structure requires the code to go to confession, recite ten Hail Marys and five Our Fathers before receiving the Almighty's state-changing grace, mercy and peace; failure to do so will result in immediate confinement in purgatory, and possibly damnation. I'm not being hyperbolic albeit a bit facetious about this. "Data" itself is the problem: it is subjective, and so requires a lot of tinkering for a programmer to grok. That tinkering transforms it into information, but only in the programmer's head, which is less than ideal since it's outside of the program. If that information fades away, it must again be rebuilt (or worse, be short circuited so a hotfix can go out to prod). > and that statefulness is fine to freely sprinkle throughout your program. I agree much more with this, with the condition that shared statefulness sprinkled throughout is the problem. An object should be treated with the same respect we would show a VM or even a person: decent ones would not tolerate having an external entity reach in and manipulate things like memory, nor would they be so care-free as to depend on and trust literally everything they are told. I think the big problem here is that few or no OO languages are powerful enough to allow for this kind of arrangement to be the norm. But it is a valid criticism and should be taken more seriously by OO language designers. EDIT: I wanted to add one more thought, which is that when I'm working on a new feature or new codebase, I tend to model the problem first in a procedural style. Then I rewrite it as objects. I've had very little luck trying to do objects first (though I think this is just me), because decomposing the problem space is just hard to do de novo. But once I understand the problem, it's easier to see the constraints and issues. Also, certain code that doesn't need to worry about things like noisy channels (e.g. Dijkstra's shortest path algorithm) is probably a good candidate for non-OO code. OO is good for the large amount of software that interacts with people across multiple machines and environments, rather than isolated from everything in just a single machine.