6 ms·
It took me a long time to realize that I actually prefer not using classes. It's the small things in programming that can make a huge difference. It always fel
by grumblingdev 3y ago
It took me a long time to realize that I actually prefer not using classes.
It's the small things in programming that can make a huge difference. It always felt like I should be using the C++ way because it was slightly dry-er and looked nicer, and I could have all these OO features.
Like when you see `int get_numer(void *r)` or Python's `def get_numer(self)`, or Go's `func (rational *Rational) GetNumer() (n int)`, you think: "how silly, why does the object pointer need to be in scope, just use `this`". But this tiny thing is liberating. It allows you to see that everything is just functions operating on data. And that a "method", is just a function that is taking _the entire object_ as a parameter...and hence a dependency. Which allows you to think: hmm, does this function really need to depend on the entire object...maybe it can be a separate utility function all by itself without any connection to the class. And maybe it doesn't actually need access to any of the other functions in the class...and maybe the class could be split up...etc.
I just watched a [talk](1) by Alan Kay the inventor of SmallTalk and the phrase "object-oriented" who never stops shitting on C++.
Yet OO is still absolutely everywhere.
[1]: https://www.youtube.com/watch?v=oKg1hTOQXoY https://www.youtube.com/watch?v=oKg1hTOQXoY
- danhau 3y agoThank you. You put into words what I was thinking. The best paradigm in programming has to be procedural style. The cleanest code I’ve written and read has always been procedural. I even maintain a monster VBA Word Macro at work and - after some much needed refactoring - its pretty easy to understand (ignoring VBA’s warts). The flow of logic and dependencies on data is much easier to follow in procedural.
- thewix 3y agoI prefer functional. Procedural is not restrained enough and allows for mutable global state. This can go off the rails when multiple people are in the code. Functional stresses composition so functions should stay small, and ADTs allow for all effects (errors!) to be represented. Strong typing helps reduce the number of tests I need to write, as well.
- diarrhea 3y agoI think ADTs and effects system are orthogonal though. A “print” has an effect but I don’t know of languages where failure of that is propagated (for example in Rust, println doesn’t evaluate to Result).
- Waterluvian 3y agoThis is why I like Rust’s associated functions concept. It is just functions and data. But with organization of clear relationships. “These functions act on these data.” (And a bit of syntactic sugar)
- diarrhea 3y agoOne disadvantage here is taking the entire struct on each operation even if only a single part of it is needed. That can be much harder to deal with due to ownership rules (if &Mut self or all of self is taken). Free functions can be easier then.
- rezonant 3y agoAlan Kay isn't going to agree that you should simply pass structures around and use element-level procedures. Message passing as a concept is a higher order version of what you see in C++, not what you see in C. A big reason Kay dislikes C++ is because it doesn't go far enough in terms of dynamic dispatch and the ability to substitute implementations dynamically at runtime. One way to put it is: C++ makes decisions at compile time that should be made at runtime. What you are saying certainly applies neatly to getters and setters, but consider that classes are used for far more complex use cases than as a simple data holder. Indeed, if all you need is to store two integer components, you might not need a class. Of course, in Smalltalk and it's ilk, you only have objects. So there is no opportunity to access data from within a structure (externally) without passing messages.
- chadcmulligan 3y agoIndeed, and C++ was initially a preprocessor (CFront) by Stroustrup, dynamic dispatch was considered too slow at the time, so c++ style objects and methods were faster and as a faster version of simula [1]. Objective C was an alternative that used something closer to smalltalk but it was slower and a few years later. C++ was first released in 1983 internally at AT&T, and for perspective, the 286 was released around the same time with a clock speed of 8 MHz. [1] https://en.wikipedia.org/wiki/C%2B%2B https://en.wikipedia.org/wiki/C%2B%2B
- up2isomorphism 3y agoWhat he has said applies to any member functions. It is a nuance to have to add a parameter, but certainly it does not only apply to getter and setters. Dynamic dispatchinga are not necessarily evil, but that also doesn’t exclude C. Not sure how this idea C++ is inherently more dynamic or static comes from. For a ling time you can implement all the dynamic behavior of C++ in C, and it took C++ ‘s template as expressive as C macros. Sometimes, less is more.that’s where C++ never gets right.
- leeman2016 3y agoHow would you do DI abstractions using just functions? (Honest question) I am so much used to the idea of swapping out functionality (set of functions and a state). For example, just recently I had to swap out an Excel spreadsheet reader/writer library in Go for performance reasons, and would have bled a lot (due to refactoring) had it not been encapsulated as an interface.
- shortrounddev2 3y agoFunction pointers in a struct
- winstonewert 3y agoHow would either approach be different? In both cases you would have to rewrite the code that directly interacts with your Excel library.
- yCombLinks 3y agoAnd a well defined class defines the structure and valid permutations for that data in a clear and easy to find place.
- billfruit 3y agoAnd allows to create any number of objects of its kind without explicitly needing to allocate memory for the objects. That is a major advantage I think, without that the code will get cluttered with all the memory allocation/deallocation going on explicitly.
- stabbles 3y agoAlso Bjarne Strostroup said that this (pun intended) was a mistake, since often there is no "most important" object. If you want to do generic programming, it's much better to write f(a, b) instead of a.f(b). Suppose I'm writing a generic algorithm that needs some function distance(A a, B b) for generic A and B. Distance is probably a commutative function, why would a.distance(b) be better than b.distance(a)? It's arbitrary. Secondly, if functions are first-class citizens, and I don't "own" the types A and B, it's much easier to implement distance(A a, B b) myself than it is to extend the types A and B with a member function `distance`.
- commonlisp94 3y agoIt would make a huge difference if that style were used more in practice. OOP is just functions that are only polymorphic in 1 argument, when you want it on all of them.
- coldtea 3y agoMultiple dispatch for the win...
- bobboies 3y agoAlso see: Effective C++ (by Scott Meyers) Item 23: “Prefer non-member non-friend functions to member functions. Doing so increases encapsulation, packaging flexibility, and functional extensibility.” The text has a lot more detail but that’s a brief summary. Folks just need to read the literature then this sort of knowledge would be in common use. One downside of language evolution is a lot of people focusing on new language features, etc, but then some of this older, important knowledge gets skipped over.
- iraqmtpizza 3y agois it not apparent that a.distance(b) and b.distance(a) should both exist? if they don't, the API is poorly designed
- mckravchyk 3y agoI have tried to sell myself on using functions (not pure functions though but ones that accept an object and modify it) but ultimately sticked to OO. I try to find the balance in-between, if something is processing heavy it's going to be a function, but if it does not do much by itself and relies on a lot of state, it's going to be a method in a class - which may call a function or a few. The biggest benefit is the structure, you get an API that can control any aspect of the app out of the box without the need to redeclare stuff.
- cryptonector 3y agoNowadays OO is seen as a mistake. Interfaces are much better. What's the difference? It's this: no inheritance. Still, with interfaces your complaints remain unsatisfied. But add generic functions and then you can have the mix of OO-like and not-OO-like APIs you have in mind.
- iraqmtpizza 3y agoI don't want to imagine something like gson or sun.tools.tree without inheritance. That would be a disaster. Composition? AssignShiftLeftExpression has-a AssignOpExpression which has-a BinaryAssignExpression which has-a BinaryExpression which has-a UnaryExpression which has-a Expression which has-a Node? return this.assignOpExpression.binaryAssignExpression.binaryExpression.unaryExpression.expression.node.op; or return this.op
- cryptonector 3y agoYou want "sum types", aka "algebraic types".
- iraqmtpizza 3y agoI don't want them; you want them.
- cryptonector 3y agoHeh, it's a manner of speaking. As in "the solution is to use sum types". Obviously i can't make you want that.
- 38 3y agoyou cant reuse names with top line functions: package hello type cat int func greet(c cat) cat { return c + 1 } type dog string func greet(d dog) dog { // greet redeclared in this block return d + "one" } but you can with methods: package hello type cat int func (c cat) greet() cat { return c + 1 } type dog string func (d dog) greet() dog { return d + "one" }
- diarrhea 3y agoI don’t think that’s a strong argument. It’s just function overloading that some languages enable, others don’t. Not a categorical difference.
- 38 3y agoOK please show me the equivalent code in C then
- tredre3 3y agoIt's possible with _Generic but there's a lot of caveats. Any seasoned C dev would just have two functions with a prefix or suffix, like cat_greet() and dog_greet(). But if you insist, it would look something like: typedef ... cat; typedef ... dog; void cat_greet(cat *obj) {/*do stuff */} void dog_greet(dog *obj) {/*do stuff */} #define greet(X) _Generic((X), cat: cat_greet, dog: dog_greet)(X) int main(...) { cat mycat; dog mydog; greet(&mycat); greet(&mydog); return 0; }
- 38 3y agothat is really, really ugly. using methods, I dont have to mess with function prefixes, and I dont have to pollute the global scope.
- ElegantBeef 3y ago
- bluGill 3y agoOO does not come from Smalltalk. Simula was the inspiration for OO in C++.
- rewgs 3y agoI'm a self-taught programmer, and I'm the lone programmer at a small non-tech company. I've mostly learned by building, and by reading documentation on language features, libraries, etc when I get stuck. I was pretty aware of "tutorial hell" pretty early on and thus avoided it, which by proxy meant I avoided most OOP dogma. I probably worked a lot harder than necessary and reinvented the wheel a few times early on, but at this point I think I write decent code, though I admit I'm somewhat siloed (e.g. I've never been subjected to a professional code review). I recently watched a talk on just what precisely Functional Programming is, and the whole time I just thought "wait, doesn't everyone do this? Isn't this just the overwhelmingly obvious way to write code?" Around the same time, I found myself running into some very OO projects, and simply couldn't understand the actual reason for all of the ceremony and ridiculous amounts of encapsulation. And this wasn't in Java, which of course is infamous for its seemingly infinite amount of ceremony -- this was the documentation of a widely-used GUI library for Python. Like, to my mind, classes are useful when you need to create objects whose values are unknown, i.e. the analogy where the class is a "cookie cutter" which creates the cookies. Classes are perfectly fine for that use-case but by no means should it be that which forms the backbone of your project; my (admittedly limited) view is that stand-alone functions are far and away the lowest-overhead and reliable device for composing parts of a system. I really get the feeling that OOP is sort of like how synths were in the 80s -- everyone just kinda lost their minds for a second and used them everywhere, whether it made sense for the music or not. But from what I can tell, OOP just isn't really the right paradigm for a whole bunch of projects, and often the design of the code vs the design of the thing feels like ramming a square peg into a round hole. Edit: all that said, if the above smacks of the Dunning-Krueger effect, I'd love to be made aware of that.
- grumblingdev 3y agoBasic functional programming concepts are good, but it can easily go off the rails when people take it too far.