I've encountered this code a lot in C, and every time I thought that this style of
> stuff_do_something(stuff, some_parameter)
_is_ OOP and identical to
> stuff.do_something(some_parameter)
, although in language without support for OOP (of course, you can hack OOP on top of C with preprocessors and stuff, but let's pretend we forgot about that embarrassing abominations). And every time I saw that I wished that I could type the second one instead, just because of how more natural it feels.
Exactly. OOP is:
a) A code naming/organisational issue
b) Some formalisation of structure, so that developers can specify what is/isn't allowed in their code.
Both of thise have advantages and disadvantages, and the structure can be over-formalised and made too complex. But this doesn't make OOP bad. It makes bad design bad.
Well yes, when you redefine "OOP" to mean "well-organized code," then all well-organized code is object-oriented. That's not what people mean when they refer to OOP though -- they're talking about data+behavior encapsulation, inheritance, message-passing, etc.
I don't disagree with the author's sentiment about inheritance (or OOP in general), being used and taught wrongly, but I still think it's a necessary tool to have available.
Without inheritance, as soon as you want to cast mixer to something else, you're either relying on binary wizardry by the compiler (reinterpret_cast. ie, undefined behaviour), or you need to break encapsulation to copy the fields over from one object to another.
Of course we could use an opaque pointer to our objects instead, but if your solution is to cast anything to (void*), where could that possibly go wrong?
OOP inheritance is pretty much doing that, but in a constrained, statically enforced way, which prevents programmers from making elementary mistakes or trying to make assumptions about the way hardware works.
Of course, the last example of the singleton works, but it also doesn't help to explain why we might use singletons in OOP. If we assume we have both a hardware mixer and a soft mixer which both have the set volume method, we wan't polymorphism so we can treat them equally. In the author's solution, we must treat each specially at the call site, which also violates our open/closed principle - we want to add new mixers without hacking (and breaking) the existing code.
Still, I pretty much agree that we should rethink the way these are taught. Perhaps every student should write his own OOP language before being let loose with it.
You're describing subtyping which, like modularity, does not require inheritance or really any other part of "OOP".
I think that supports the article's point that C++, Java, etc. only support certain language features in an ad-hoc way tied to their object systems, making them, among other things, poor teaching languages.
> Without inheritance, as soon as you want to cast mixer to something else, you're either relying on binary wizardry by the compiler
Why would you want to cast mixer to something else? What real-life task does it solve?
> If we assume we have both a hardware mixer and a soft mixer which both have the set volume method, we wan't polymorphism so we can treat them equally.
Polymorphism isn't OOP feature. You can have overloaded `mixer_set(struct, int)` without any OOP wizardry involved.
Let's suppose you want labels, text boxes, and buttons on a form.
Now you can do this without OOP or making a huge chunk of form code, but OOP is a natural fit. Especially if we may want to add pictures or video in the future.
Are you saying all these outlets (labels, text box, buttons) are inheriting from a base FormObject that can be rendered?
That would work without OOP. Make a FormRender component that manages that logic (being rendered in a form) and other components, one that manages a keyboard, other that can be clicked and handle hover, touch states, etc.
Sure, but routing get's complex in your solution. The point was OOP is a natural fit for collections of different types in some context ex:GUI, Simulation, etc. The classic OOP case is probably a MUD where OO code ends up being very straightforward.
IMO, the problem is it works really well in so many contexts that people end up trying to use it everywhere.
OO in MUDs seemed so natural to me, I think, because they're closer to the actor model that OO was really designed for. Instead of "methods", many objects in MUDs (depending on the system, I used to know more when I was research writing a MUD engine in erlang), respond to messages in more of a smalltalk-style.
ECS works well when you can predefine everything ahead of time. But, it must be extended for every additional type.
As to messaging, consider what happens when you add JavaScript that can change the DOM. Now, you need to pass messages in both directions not just What is your Height- Width, but also my Height-Width has changed. Animated Gifs or Videos now want to update things in real time, and other windows can cover parts of your view port.
And let's not forget about transparency.
PS: ECS can still work, but it starts getting really complex.
> Why would you want to cast mixer to something else?
Did you ever heard about interfaces? You can have a IMixer interface, a Mixer baseclass and some concrete mixer classes depending on your task. Like null-mixer for mocking, software mixer for legacy computers, hardware mixer with GPU support, etc. Use your imagination, Luke!
What baffles me about OOP is that massive aspects of how to use it have changed, and that change happened after it gained popularity as a good technique. How could it have become popular when people were doing it wrong? The article mentions inheritance, but there's also heavy vs light classes. When I was taught OOP, an object knew how to do all the things to itself. It could draw itself on the screen, modify itself, save itself to a file, etc. Now that's turned on its head and is bad practice.
So if OOP was great with inheritance and heavy classes, how can it now be bad with inheritance and heavy classes? Were the original OOP evangelists doing it wrong and promoting something they hadn't actually tried enough to discover its problems (ie hype)? Was it just misunderstood and inheritance has always been known to be something to avoid and light (single public method) classes always been a good idea but computer science professors didn't understand it?
>What baffles me about OOP is that massive aspects of how to use it have changed, and that change happened after it gained popularity as a good technique. How could it have become popular when people were doing it wrong?
Your arguments boils down to "how come something that had utility X at some point in time, have less than X at a later point", which frankly I don't think is surprising at all, nor logically contradictory (which seems to be the assumption).
How is that surprising? It happens with most emerging programming paradigms/technologies.
They first get popularity for some new benefits they bring, and then, as the dust settles, the community finds out the best ways to do something, shapes their best practices etc.
Two examples:
1) JAVA. Initial lure: runs anywhere, easier to use than C/C++, fast, etc. Java, then went through the crazy J2EE, XML everywhere, etc phases, and after that they found a balance on how you should write code, including adopting more functional styles now.
2) PHP. Initial lure: dead easy to install, comes with every function webdevs needed at the time, runs on all cheap hosts, easy syntax. Community used crappy coding practices, bad idioms etc, but slowly improved their ways, found new ways to structure code, etc.
>So if OOP was great with inheritance and heavy classes, how can it now be bad with inheritance and heavy classes?
That was then, and this is now.
Neither CPUs, nor the available programming languages, nor needs, nor our collective understanding of programming is the same.
The same way "Finding Nemo" might be good when you're 10 (and given your other options at the time), but at 30 you have a lot more movie options...
1) CPUs: are now multicore, and OO is NOT the best way to get advantage of them (push towards pure, functional, etc).
2) Available programming languages: then, at least the mainstream ones where ...assembler, plain Pascal, C, etc, compared to which OO was a novel and handy way to structure programs. Now we have much more options.
3) Needs: back then --80s, 90s-- it was the golden age of Desktop GUI programming, for which OO is a quite good match. For web programming and the kind of work we do now, not so much.
4) Collective understanding of programming: back when a closure was something exotic, and functional concepts totally alien to most programmers, even OO was too foreign for some, but at least they understood it. So it was a good match for the average programmer then. Nowadays programmers come out of universities, or learn on their own with the millions of internet resources, and have a match better understanding of programming idioms, exposure to new ways to code, etc.
"Was it just misunderstood and inheritance has always been known to be something to avoid and light (single public method) classes always been a good idea but computer science professors didn't understand it?"
Could be. Or it could be that it's possible to write working programs with many different methodologies and 90% of what developers argue about is fashion not substance...
Initially there was machine code (well, manual wiring of computers). Then there was assembly, and that worked. Then there were Fortran and Lisp and Algol, and somehow we still needed more languages and methodologies.
GOTO was fine, until it wasn't. Then we had structured programming and things were better.
Every language and methodology has had its pros and cons, and in general the trend has been towards "better" (by some dimension in a large, multi-dimensional space) languages and methods.
Heavy classes were an improvement over what existed before them, not because they were heavy classes, but because what was before them was still (often) unstructured code.
Light classes, moving things up a level so that, for instance, your graphical object doesn't draw itself, but instead knows enough to inform the drawing class how to draw it, is better in one particular regard: same object can be rendered in any system by updating the layer above it. So instead of updating 100 drawn objects, you now update 1 drawing object.
That doesn't make heavy classes "wrong", they were fine at their time (probably more performant, and almost certainly an improvement in some ways over what existed before them). But light classes with some degree of abstraction is often better (too much is still a problem, damn you <person I inherited this project from>). Better, in this case: shorter code, fewer "moving" parts, easier to reason about, easier to manipulate. It is potentially less performant (depending on language and design choices), but that's not a guarantee.
There has definitely been continued learning of how to best use OOP, as with any programming paradigm. While they all existed individually before-hand, the SOLID principles as a collection weren't formalized until the 2000's. [0] For instance, "heavy" classes is likely a clear break of Single Responsibility and is probably also a break of Interface Segregation. I also can't recall ever seeing an introduction to inheritance that doesn't break the Liskov substitution principle -- sometimes subtly, but usually quite overtly.
I think the large issue with OOP education is that it is still being taught as a set of capabilities without the appropriate constraints (SOLID) that have been learned by engineers under fire.
Perhaps the question isn't binary, but rather have a whole range of answers. Heavy classes was probably better than the procedural code that came before it, but light classes end up more easy to reason about in the long run. I know for a fact that I have made that progression as I gained experience.
We actually used to write "OO" C code like this - lots of it - back in the Xt Toolkit Intrinsics days. It sucked. Writing GUI systems in C++ was a massive improvement. Sometimes syntax matters.
I've been saying this for years. Historically, yes, OOP has its own roots. But for all effects and purposes, C++ with headers controlling visibility is the grandfather of OOP. If you got that, then you've got OOP basics. (And, as the author points out, 90% of what you'll ever need)
There's another way of "going beyond OOP" that bears some thought, and that's growing a program by incrementally adding functions, then grouping the functions into modules.
This is actually the same point as the author is making, but looking at it from the other way. It occurs to me that if we started with writing code using pure functions, over time those functions would naturally accumulate into cohesive modules. A methodology that taught this could well end up being the TDD of the next generation of coders.
> when people are talking about OO objects they mean data+behaviour
The distinction between behavior being "a set of methods defined inside a class", and "a set of methods whose first param is a particular type of struct" is not that important.
The distinction between "a set of methods with privileged access to encapsulated internal state of a class" and "a set of functions whose first param is a particular type of struct, and operate on members of that struct that everyone can access" is pretty critical though.
Definitely, and encapsulation is one of the good things you get out of well designed OO. But I don't think it's that relevant in regards to objects as data+behavior, and while it leads to much better designs, I don't think it's required—hence all of the C libraries written in a very OO style. So: yes, but not really relevant to the point being discussed.
The point is not a lack of objects but a lack of OOP. Yes, there are "objects" but there is also a separation between objects and methods etc..
The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume() because in the method scope you have access to the internals of the "m" object, not just the interface.
With mixer_set_volume(m, .08) you have ".08" and "m" but only the interface "m" exposes anywhere else. Thus it becomes easier to reason about effects.
Can you expand on this? I'm not sure I follow, but maybe there's something particular to C++ here that I don't get as I'm not well-versed in it.
>> The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume()
Isn't that the point? That you shouldn't have to care what happens inside set_volume? If mixer was designed and implemented well, then set_volume (along with the rest of its interface methods) should consistently and correctly handle its internal state.
Whether you are dealing with OOP, functional, or some other pattern it is always the point to create abstractions which you and others can forget about how they work.
Thus m->set_volume(.08) and set_volume(m, .08) should not require you to understand what happens internally as the user of either.
More generally in programming that is the point.
My only point is that of scope and essentially search space -- set_volume(m, .08) can only access the public interface of "m". While, m->set_volume(.08) could do almost anything to m. In a perfect world, with perfect programmers thinking the same way about what "set_volume" means or should do the separation of state and action would be less valuable.
This not to say OOP is > Functional or vice-versa. I think there is a place for both but that is a MUCH larger and more nuanced conversation :)
"Object" is such a generic term that one can call anything an object. However, in the OOP world, an entity is an object if it has been defined or created like an object in the language's syntax. So the answer to your question is: we don't know, show us its declaration.
But the truth is that one can write many different stories on this line. One can tell the OO story in which m is an object, one can tell the FP story in which m is an IOMonad, one can tell the procedural story in which m is a record.
All of these stories are more or less equally good if it's a single line of code. But on a whole program, some of them can be more or less believable at certain points. For instance, when the OOP story tells us that integers are objects and that "+" is a message to which integers answer by given the result of an addition, this part of the story can seem a little far-fetched for some people.
Is there such a thing as the "best story in the world"? Definitely not. There's not even a "my favourite story" for most people, but instead a "my favourite love story" and a "my favourite horror story", and so on. How does one come up with a favourite story? By reading stories.
A value, with a type. Nothing else. Stop highjacking everything into your OO religion. If you take everything of a value that originated elsewhere, there will be nothing left in the OOD.
> Stop highjacking everything into your OO religion.
The parent to your post seemed to have a legitimate question, and you're being snarky. You could have left out everything after the first sentence or even explained what that means in practice.
The word "object" comes loaded with a shit ton of ideology. There is absolutely no need to rename the long established terms to simply please a bunch of religious zealots.
Many of your comments have been breaking the HN guidelines. Please stop being uncivil and calling names.
You clearly know enough to make substantive contributions, which is great for this forum, but it you keep being rude, we'll have to ban you. So please don't.
I could take that text more seriously if it at least discussed the limitations of the most powerful embodiments of OOP that we have today and not the accidental limitations of languages that for one reason or the other ended up not as good as they could have and force you to jump through hoops, as evidenced by the very mentions of classes, constructors, private methods etc., none of which are fundamental to OOP but all of which were made fundamental to certain languages.
I agree, but OTH you can be a Platonist and refute every criticism with this argument. In the end the OOP you have is the OOP that has been implemented, so judging the implementations we have to work on is more down to earth.
True. But when it comes to learning the basics of programming, you can always pick better languages. There doesn't appear to a shortage of pedagogically useful ones.
I agree with this post, and especially the main focus of the post, which seems to be: teach the basics before bogging students down in the crazy terminology-heavy industry we live in.
Yes absolutely. I learned OO with programming in the early 90s, around the birth of the OO religion. So it's definitely in my mental toolbox if needed... But in practice I'd say I rarely, if ever, need it. The only time it's come up in the last 5 years was during a job interview, when I mentally did an eye roll and thought "oh god OOP stuff again" Granted, I do more glue programming and scripting, vs full-fledged app development. I'm sure if I was doing Android apps I'd be using ActiveWindowAppAdapterFactoryConstructor (q.v., programming in the kingdom of nouns) but thankfully that's not something I have to do (yet).
There's a lot of non-OOP C++ outside of the classroom ;)
Procedural C++ is big in systems (and that's not just plain C compiled with a C++ compiler). Templates, strings, built-in containers and smart pointers are very popular and useful.
No one technique scales on its own. Assembly, macro assembly, expressions, functions, classes, inheritance, templates, closures, data flow, and even message passing if you want performance.
Everyone keeps looking for the silver bullet, but ends up with answers that are simple, easy, and wrong.
If there is a bullet at all I think it is accepting that software architecture is made up of all these things + tools that give lots of extra information easily + interactivity and small iteration times.
I think when most people are being overwhelmed with complexity they want a silver bullet so badly they will believe that there is one against all evidence to the contrary.
The author seems to genuinely not know that this is the original motivation for C++ -- to provide syntactic sugar and additional type safety to the very techniques he describes, which were (and still are) widely used in C. It is as if I am reading a "Software The Onion". Hmm...checks date...
Okaaaaaaay, so if we left everything what OOP gived us (properties^1, generics, lot of good patterns^2, lot of abstractions for generic things, like interface^3, events, higher level datastructures, delegates, namespaces, modules^4 etc.) then we get C? Omg, thank you Captian Obvious! Learn OOP first.
What a bullshit article.
1) C++ for OO example? Seriously, why??? Grab a modern language who have properties at least. Even Delphi had it!
2) IMHO without OO patterns OOP worth nothing
3) I know, you can do interfaces in C++ with pure virtual classes, but you miss the abstraction behind that.
4) Java JAR, .NET assemblies, no matter what they called.
Not sure I understand the point of this article, especially in the context of C++ where encapsulation with classes helps avoiding things like memory leaks, dangling pointers and co. If you feel you need to code C style, use C, with all the problem it might incur.
Personally, think object oriented programming gets a fair amount "right". That isn't to say there aren't problems.
Specifically, classes as a mechanism of packaging data together with all the functions that act on that data is a good idea. Additionally, I think classes are more intuitive and generally tend to be syntactically clearer than their functional equivalent, namespace factories (though less semantically explicit). Fluent style chaining of method invokation is also a lot easier to read than nested function calls in most languages.
The biggest issue I have with classes is the concept of constructors and destructors. These are semantically unclear and basically break multiple inheritance. Aside from that, in dynamic languages classes also encourage people to write code that checks class type, which is both brittle and potentially limiting, depending on the context.
You could generalize the described approach even further and have a single global function called 'message', so that your entire app consists entirely of statements like:
message(receiver, msg)
Sure, it's encapsulated, but it's hardly modular. The author knew not to do this because he's undoubtedly already had a lot of experience with OOP. So writing OOP-like functions comes easy to him. I'm not sure that would be the case for a beginner, who could also go to the other extreme and have a different function for everything, even if two methods differ only in their arguments:
add_one_to(number)
add two_to(number)
etc.
The point of OOP is that it provides some guidance in navigating between the two extremes, and tools to manage modularity and redundancy (like inheritance and composition). Encapsulation isn't the sole benefit.
71 comments
[ 5.2 ms ] story [ 138 ms ] thread> stuff_do_something(stuff, some_parameter)
_is_ OOP and identical to
> stuff.do_something(some_parameter)
, although in language without support for OOP (of course, you can hack OOP on top of C with preprocessors and stuff, but let's pretend we forgot about that embarrassing abominations). And every time I saw that I wished that I could type the second one instead, just because of how more natural it feels.
Both of thise have advantages and disadvantages, and the structure can be over-formalised and made too complex. But this doesn't make OOP bad. It makes bad design bad.
Without inheritance, as soon as you want to cast mixer to something else, you're either relying on binary wizardry by the compiler (reinterpret_cast. ie, undefined behaviour), or you need to break encapsulation to copy the fields over from one object to another.
Of course we could use an opaque pointer to our objects instead, but if your solution is to cast anything to (void*), where could that possibly go wrong?
OOP inheritance is pretty much doing that, but in a constrained, statically enforced way, which prevents programmers from making elementary mistakes or trying to make assumptions about the way hardware works.
Of course, the last example of the singleton works, but it also doesn't help to explain why we might use singletons in OOP. If we assume we have both a hardware mixer and a soft mixer which both have the set volume method, we wan't polymorphism so we can treat them equally. In the author's solution, we must treat each specially at the call site, which also violates our open/closed principle - we want to add new mixers without hacking (and breaking) the existing code.
Still, I pretty much agree that we should rethink the way these are taught. Perhaps every student should write his own OOP language before being let loose with it.
I think that supports the article's point that C++, Java, etc. only support certain language features in an ad-hoc way tied to their object systems, making them, among other things, poor teaching languages.
Why would you want to cast mixer to something else? What real-life task does it solve?
> If we assume we have both a hardware mixer and a soft mixer which both have the set volume method, we wan't polymorphism so we can treat them equally.
Polymorphism isn't OOP feature. You can have overloaded `mixer_set(struct, int)` without any OOP wizardry involved.
Now you can do this without OOP or making a huge chunk of form code, but OOP is a natural fit. Especially if we may want to add pictures or video in the future.
That would work without OOP. Make a FormRender component that manages that logic (being rendered in a form) and other components, one that manages a keyboard, other that can be clicked and handle hover, touch states, etc.
IMO, the problem is it works really well in so many contexts that people end up trying to use it everywhere.
As to messaging, consider what happens when you add JavaScript that can change the DOM. Now, you need to pass messages in both directions not just What is your Height- Width, but also my Height-Width has changed. Animated Gifs or Videos now want to update things in real time, and other windows can cover parts of your view port.
And let's not forget about transparency.
PS: ECS can still work, but it starts getting really complex.
Did you ever heard about interfaces? You can have a IMixer interface, a Mixer baseclass and some concrete mixer classes depending on your task. Like null-mixer for mocking, software mixer for legacy computers, hardware mixer with GPU support, etc. Use your imagination, Luke!
So if OOP was great with inheritance and heavy classes, how can it now be bad with inheritance and heavy classes? Were the original OOP evangelists doing it wrong and promoting something they hadn't actually tried enough to discover its problems (ie hype)? Was it just misunderstood and inheritance has always been known to be something to avoid and light (single public method) classes always been a good idea but computer science professors didn't understand it?
Your arguments boils down to "how come something that had utility X at some point in time, have less than X at a later point", which frankly I don't think is surprising at all, nor logically contradictory (which seems to be the assumption).
How is that surprising? It happens with most emerging programming paradigms/technologies.
They first get popularity for some new benefits they bring, and then, as the dust settles, the community finds out the best ways to do something, shapes their best practices etc.
Two examples:
1) JAVA. Initial lure: runs anywhere, easier to use than C/C++, fast, etc. Java, then went through the crazy J2EE, XML everywhere, etc phases, and after that they found a balance on how you should write code, including adopting more functional styles now.
2) PHP. Initial lure: dead easy to install, comes with every function webdevs needed at the time, runs on all cheap hosts, easy syntax. Community used crappy coding practices, bad idioms etc, but slowly improved their ways, found new ways to structure code, etc.
>So if OOP was great with inheritance and heavy classes, how can it now be bad with inheritance and heavy classes?
That was then, and this is now.
Neither CPUs, nor the available programming languages, nor needs, nor our collective understanding of programming is the same.
The same way "Finding Nemo" might be good when you're 10 (and given your other options at the time), but at 30 you have a lot more movie options...
1) CPUs: are now multicore, and OO is NOT the best way to get advantage of them (push towards pure, functional, etc).
2) Available programming languages: then, at least the mainstream ones where ...assembler, plain Pascal, C, etc, compared to which OO was a novel and handy way to structure programs. Now we have much more options.
3) Needs: back then --80s, 90s-- it was the golden age of Desktop GUI programming, for which OO is a quite good match. For web programming and the kind of work we do now, not so much.
4) Collective understanding of programming: back when a closure was something exotic, and functional concepts totally alien to most programmers, even OO was too foreign for some, but at least they understood it. So it was a good match for the average programmer then. Nowadays programmers come out of universities, or learn on their own with the millions of internet resources, and have a match better understanding of programming idioms, exposure to new ways to code, etc.
People start using it and it works fine. Then the codebase grows and everything gets unmanageable...
Could be. Or it could be that it's possible to write working programs with many different methodologies and 90% of what developers argue about is fashion not substance...
GOTO was fine, until it wasn't. Then we had structured programming and things were better.
Every language and methodology has had its pros and cons, and in general the trend has been towards "better" (by some dimension in a large, multi-dimensional space) languages and methods.
Heavy classes were an improvement over what existed before them, not because they were heavy classes, but because what was before them was still (often) unstructured code.
Light classes, moving things up a level so that, for instance, your graphical object doesn't draw itself, but instead knows enough to inform the drawing class how to draw it, is better in one particular regard: same object can be rendered in any system by updating the layer above it. So instead of updating 100 drawn objects, you now update 1 drawing object.
That doesn't make heavy classes "wrong", they were fine at their time (probably more performant, and almost certainly an improvement in some ways over what existed before them). But light classes with some degree of abstraction is often better (too much is still a problem, damn you <person I inherited this project from>). Better, in this case: shorter code, fewer "moving" parts, easier to reason about, easier to manipulate. It is potentially less performant (depending on language and design choices), but that's not a guarantee.
I think the large issue with OOP education is that it is still being taught as a set of capabilities without the appropriate constraints (SOLID) that have been learned by engineers under fire.
[0] https://en.wikipedia.org/wiki/SOLID_%28object-oriented_desig...
There's another way of "going beyond OOP" that bears some thought, and that's growing a program by incrementally adding functions, then grouping the functions into modules.
This is actually the same point as the author is making, but looking at it from the other way. It occurs to me that if we started with writing code using pure functions, over time those functions would naturally accumulate into cohesive modules. A methodology that taught this could well end up being the TDD of the next generation of coders.
(Yes, you cannot inherit all kinds of objects – but you definitely can inherit any kind of struct, POD or not.)
Usually when people are talking about OO objects they mean data+behaviour.
The distinction between behavior being "a set of methods defined inside a class", and "a set of methods whose first param is a particular type of struct" is not that important.
The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume() because in the method scope you have access to the internals of the "m" object, not just the interface.
With mixer_set_volume(m, .08) you have ".08" and "m" but only the interface "m" exposes anywhere else. Thus it becomes easier to reason about effects.
>> The issue with m->set_volume(.08) is it becomes difficult to tell what happens inside set_volume()
Isn't that the point? That you shouldn't have to care what happens inside set_volume? If mixer was designed and implemented well, then set_volume (along with the rest of its interface methods) should consistently and correctly handle its internal state.
Thus m->set_volume(.08) and set_volume(m, .08) should not require you to understand what happens internally as the user of either.
More generally in programming that is the point.
My only point is that of scope and essentially search space -- set_volume(m, .08) can only access the public interface of "m". While, m->set_volume(.08) could do almost anything to m. In a perfect world, with perfect programmers thinking the same way about what "set_volume" means or should do the separation of state and action would be less valuable.
This not to say OOP is > Functional or vice-versa. I think there is a place for both but that is a MUCH larger and more nuanced conversation :)
But the truth is that one can write many different stories on this line. One can tell the OO story in which m is an object, one can tell the FP story in which m is an IOMonad, one can tell the procedural story in which m is a record.
All of these stories are more or less equally good if it's a single line of code. But on a whole program, some of them can be more or less believable at certain points. For instance, when the OOP story tells us that integers are objects and that "+" is a message to which integers answer by given the result of an addition, this part of the story can seem a little far-fetched for some people.
Is there such a thing as the "best story in the world"? Definitely not. There's not even a "my favourite story" for most people, but instead a "my favourite love story" and a "my favourite horror story", and so on. How does one come up with a favourite story? By reading stories.
The parent to your post seemed to have a legitimate question, and you're being snarky. You could have left out everything after the first sentence or even explained what that means in practice.
What else do we need? “Methods”? Why?
I very strongly support the proposed Unified call syntax.
There are some benefits if everything is an object, like generics.
You clearly know enough to make substantive contributions, which is great for this forum, but it you keep being rude, we'll have to ban you. So please don't.
We detached this subthread from https://news.ycombinator.com/item?id=11373716 and marked it off-topic.
Procedural C++ is big in systems (and that's not just plain C compiled with a C++ compiler). Templates, strings, built-in containers and smart pointers are very popular and useful.
If you need genericness when the size of the data doesn't change, use templates.
If you need genericness when the size of the data does change, use inheritance.
If you need genericness with respect to part of an algorithm, use lambdas.
Everyone keeps looking for the silver bullet, but ends up with answers that are simple, easy, and wrong.
If there is a bullet at all I think it is accepting that software architecture is made up of all these things + tools that give lots of extra information easily + interactivity and small iteration times.
I think when most people are being overwhelmed with complexity they want a silver bullet so badly they will believe that there is one against all evidence to the contrary.
What a bullshit article.
1) C++ for OO example? Seriously, why??? Grab a modern language who have properties at least. Even Delphi had it!
2) IMHO without OO patterns OOP worth nothing
3) I know, you can do interfaces in C++ with pure virtual classes, but you miss the abstraction behind that.
4) Java JAR, .NET assemblies, no matter what they called.
Specifically, classes as a mechanism of packaging data together with all the functions that act on that data is a good idea. Additionally, I think classes are more intuitive and generally tend to be syntactically clearer than their functional equivalent, namespace factories (though less semantically explicit). Fluent style chaining of method invokation is also a lot easier to read than nested function calls in most languages.
The biggest issue I have with classes is the concept of constructors and destructors. These are semantically unclear and basically break multiple inheritance. Aside from that, in dynamic languages classes also encourage people to write code that checks class type, which is both brittle and potentially limiting, depending on the context.