4 ms·
I just don't see much of a practical difference between these two approaches. From a functional perspective, objects are fundamentally a set of namespaced func
by cle 7y ago
I just don't see much of a practical difference between these two approaches. From a functional perspective, objects are fundamentally a set of namespaced functions that implicitly take the same state/map/JSobj as an argument bound to "this". You can pass that state in explicitly and it just becomes what the author is talking about. And you can have the same state-management nightmares with that approach, because nothing is fundamentally different. Instead of obj.method(arg1) you have namespace::function(obj, arg1). I've worked in a very large frontend codebase using this technique and it's just as much of a pain as implicitly managing state with objects, if not more of a pain b/c it's no longer idiomatic.
State management is hard. Changing how you refer to the state isn't going to make it any easier, just different.
- AnimalMuppet 7y agoThe difference with this approach is that any function can take obj as an argument. If it's pass by reference, any function can mutate obj. If obj winds up in an inconsistent state, any function in the entire codebase could have done it. With a class, however, you normally make the data members private; that is, only the member functions of the class can change them. If the data gets mangled, you now have a very small set of places to look. That difference matters when debug time comes around.
- tobr 7y agoIf you use a functional approach, you’re probably not mutating anything ever. That usually makes it trivial to find the source of a problem, because you can quickly isolate it to a specific call stack. > only the member functions of the class can change them. If the data gets mangled, you now have a very small set of places to look. But probably anything could have called those mutating methods, so you still need to consider the entire codebase.
- AnimalMuppet 7y agoAnything could have called those mutating methods. But only those mutating methods could have put the object in an inconsistent state. I therefore have to fix the method that did that, and then let anything in the entire codebase freely call those methods.
- overgard 7y agoThose limits only apply to certain languages. In typescript you can keep functions private to a module, and in functional languages you would likely return a new object instead of mutating the existing ones. Heck even in C you can enforce privacy without needing a class -- only expose certain functions in the header and pass around handles instead of the data structure directly
- tel 7y agoIf classes were just that then they'd not be so bad. They also tend to bring inheritance along with them, though, and that's a messy beast.
- chrisweekly 7y agoIt's classical inheritance in particular that's fragile and rigid and problematic.
- tel 7y agoPrototypical inheritance (which, I assume, is the other side of what you're referring to) is also weird. Prototypes eliminate classes as a special thing (relegating them to mere constructors or factories, which is fair) but they don't help with typing (compiler-enforced or logical). If A has B in its prototype chain, are all values of A able to be transparently treated as values of B? Plausibly, this works, but there's nothing that really enforces it. Prototype inheritance is just a way of not needing to repeat code. I agree it's a bit better than class inheritance, but most of the time all you need and want is composition.
- chrisweekly 7y agoWe're agreed that prototypal inheritance (at least in Javascript) is orthogonal to typing. Your question about the values of A and B seems to imply a parent:child relationship -- which is the kind of hierarchy that we're looking to avoid, right? It's straightfwd to flatten the prototype chain using a single delegate prototype. Yes, favor composition over inheritance. FWIW, I really like Eric Elliott's take on this stuff: https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a https://medium.com/javascript-scene/common-misconceptions-ab...
- frobozz 7y agoThat implicitness is part of the problem. In an object, all the functions implicitly take that state (or explicitly don't if you mark them static). So instead of namespace::function(arg1), you now have namespace::function(obj, arg1), even when your function has nothing to do with obj. From the outside, you then have to wonder which members of obj might be being read or written inside the function. If lots of those methods call other methods, which may or may not modify the state of the object, you can end up in a situation where it's hard to reason about the functions because you have to read a lot of code to work out what the inputs and outputs really are. This is the problem in the Java-style "everything must be a member of a class" approach. People have a tendency to make everything into members of objects. A function that only takes the values it needs is much easier to reason about. I think that you are right in that there isn't much practical difference between the two approaches when well-written (in particular when staticness/mutability/constness are clear). However, I find poorly written OO (e.g all methods are instance methods, local variables stored as members) to be really tricky to reason about, and all too common. I haven't had that problem with poorly-written non-OO code.
- UncleMeat 7y agoI think this can reasonably be described as a footgun rather than a bad design. Instance methods should relate to the state of the instance. If they don't that's a poor design choice. This is a pretty easy code smell to reject in code review. Perhaps Java would be cleaner if it had free functions. But this isn't so different than a big class that has a pile of static methods. C++ also addresses the "you have to wonder what members might be read or written inside the function" by encouraging the use of "const" as much as possible. You get really nice invariants preventing surprise state changes in receiver objects and other parameters through judicious use of "const", even if everything is a class or object.
- frobozz 7y ago> This is a pretty easy code smell to reject in code review. It is if the code was reviewed. If, OTOH, it just grew until something needs to be done (like the object with 60 public methods that I'm currently trying to refactor), you can have a devil of a time unpicking it. The same could be true where free functions are preferred, but it seems that most programmers seem to have grasped the idea that global variables are hard. > by encouraging the use of "const" as much as possible. I'm a big fan of const-correctness. I haven't written C++ in years, but I always used to love putting 'const' everywhere.