I’m loving this series. I appreciate your clear and straight forward way of teaching. It’d be wonderful to see a video covering the functional programming approach as well.
Nice explainer. As a college student learning modularity (yay Pascal!) in the early-to-mid 80s, this idea was not taught. I'm not sure object-oriented programming was even mentioned. When I came across OOP after college, I could not wrap my head around it. When I dove headlong into PL/SQL (and O'Reilly's PL/SQL Vol 2) is when I really started getting a better grasp. And still, I'm barely a novice.
Thanks, Jack. I teach a number of courses online these days and it took me a while to realise how many students really don't know what modularity (or encapsulation) are all about. At least, I hope this will give people some things to think about.
I'm a beginner Python developer and I have wrote code in procedural way. I was wondering why we need OOP. I love how this series explains what are the urgencies and problem that OOP mean to solve. You are a great explainer and storyteller too!
Very interesting aspects of OOP. I think what modern languages do is a totally broken OOP. It is some "pragmatical" mixture of the original ideas. It can not even be fixed because of the ecosystem which forces to "inherit" the wrong behavior. People often think OOP modularity is about using private fields with public methods but that is not true. OOP modularity is a different way of thinking. You can write low level accessors and mutators for private fields that seems to be very modular, however it is just a fake modularity with low level binding. Message sending is about high level binding: I ask/request the printer to print not just set its inner values using setters then call its print method. Etc..etc... Low level binding is bad because it leaks the inner structure of an object and leaks cause spaghetti code and reduces robustness.
Thanks for this course! I feel less alone finally. ❤ As a Smalltalk developer I had to work on other projects in other languages (C#, Python, Java) and it was always a big pain as most of developers don't understand OO, don't understand encapsulation, don't understand messaging and therefore modularity. In addition they think that OO is mainly about state and state change which is not at all a prerequisite for OO.
I think all programmers would learn something from using Smalltalk, even if they never use it for any serious projects. Anyway, many thanks for the comment.
Thankyou for this in depth exploration of object-oriented programming. I am getting hung up on this idea of message passing. In an earlier video I believe you mentioned that a message may not be a command. This gives me the impression that messages should behave more like events. So I wonder if part of the confusion with OO is just this. For example, rather than telling the black box to give you chocolate, you might just say I am hungry for chocolate.
The essential idea is that it is up to each object to "decide" how to respond to a message. The methods declare a route of communication with an object. The messages make requests to be answered by the methods. This sounds odd at first but it is really an approach to modularity. You may find it easier to think of methods as the declared "interfaces".
This is in line with how modularity and encapsulation are understood in cognitive science. Perhaps it’s no accident Fodor wrote “Modularity of Mind” in the early 80’s
I will write my first assumption of what modularity is here and see if by the end of this video, the explanation is better. Modularity is simply the ability to hold one module in the mind’s context without having to consider anything else. Modularity is not a computer science concept. It is a reality concept. All of the ideas around it exist to accomplish this. Information hiding isn’t anything really. It is simply a way to control what users (including me) have to think about when looking at this block. Imagine if all clocks were opened up and you had to solder the power source to it to start it. This is the other extreme against modularity. It works, but the amount of irrelevant information provided to the consumer can be greatly reduced. I have personally found the physical analogy to be most helpful in understanding all of the techniques around it. Now, to watch the video.
Does it matter whether the object in question is a black box or a white box regarding modularity though? I mean if an object only holds data, what's wrong with exposing the data it holds directly. For example, in C#: ``` public enum MidiNote : byte { A1, ASharp1, B1, C1 //...↓ } public readonly record struct PianoKey(decimal Frequency, MidiNote MidiNote); public readonly record struct PianoKeyboard(IEnumerable Keys); ``` All of the data on all of these objects can come out of the object the same way it went in; I guess the question comes in on: where does that data get created? In this form I'd say I'd have a function on PianoKeyboard which creates a default set of PianoKeys but by also having this constructor public, it allows an alternative implementation to be created by the caller - does this break modularity though?
If you only ever need to assign or retrieve data items, the use of private data and public accessors may seem like overkill. However, if a class's author later decides to modify the data before returning it (say a string is auto-capitalised or some tax rate is calculated) then the fact that the data is hidden makes a big difference. If a programmer outside the object relies on some data item being in a given state (e.g. a string that is lowercsae or a value having a specific tax rate) then that code will now contain errors by accessing the data item direct. But code that goes via a method will be correct because it automatically gets the changes made by the implementor of those methods. In general, there is no downside to hiding data (unless, in very time-dependent code, a programmer wants data access optimised for speed) but there might be a downside if a programmer bypasses methods since any modified implementation details will also be bypassed. Best wishes Huw
@@LearnWithHuw Thanks Huw - I have encountered this in the past where I had public fields and I wanted to raise a ValueChanging and a ValueChanged event before and after each one was set. However, with the `record` example, the values are essentially held as immutable properties - this means the underlying implementation could be changed to give these backing fields and even make them mutable without having to change anything on the caller's end. Regarding your example though about upper-casing the first letter or applying a tax rate, I'd argue that if the object is immutable, this should have been done upon construction of the object (when the value is passed-in) - whilst there would be an initial upfront performance hit of doing this (when the object is created), it would mean that the same data needn't be recalculated each time it needs to be retrieved.
Modular programming thrives on the concept of "loose coupling," keeping components independent and interactions minimal. I'm not sure why Smalltalk wraps it up in a layer of mystery-it's really just about making code easier to manage and evolve.
I think the jargon seems to make it more mysterious. When Smalltalk emerged, the assumption was that programmers would be unable to understand anything in which procedures were "unbound". So the idea of passing messages to objects was introduced as a way of "simplifying" the notion of communication with procedures via their containing objects. Over time, the jargon has become more of a hindrance than a help, however.
Good question. Difficult to answer. In principle, divide by zero (e.g. 3 / 0) is a message that sends the object 0 to the integer 3 and it can then respond appropriately, as with any other message. In practice, some exceptions might not be so easily predictable and trappable. I think modern Smalltalk implementations have various different exception-handling capabilities. There is an interesting discussion of that here: wiki.c2.com/?SmalltalkAndExceptions I'll need to look into this more fully and maybe do separate video on the subject later on. The whole history of exception handling is probably worth looking into, in fact!
Technically this is a breach of contract since the caller is relying on unspecified internal behavior (the object being passed being altered internally) but I see your point. No bad though. So far I've only seen a couple of your videos and although I find them a bit drawn out, that might be a good thing for those not so familiar with what you should _actually_ be doing with OOP, which seems to be a lot of programmers. I had a suspicions that the original ideas were actually well thought out but had their various implementations broken (cough, C++, cough) and it looks like those were right.
Yeah the change in num is part of the method's specification. Changing that is not changing the "internals" of the object, it's a change to the API. The same programmer could've messed up the return value as well, so you won't be the least bit safer by relying on that. I still prefer return values over inout arguments but that's just because the caller side looks more explicit.
So let me get this straight : You can only pass by value into a function of an object in Smalltalk? How about passing arrays of any dimensions? In C++ that is done by reference since you wouldn't get to the "next value" in the index of the array without knowing where the whole array starts. How is that done in Smalltalk? Or passing arrays as pointers where you can have dynamically sized arrays? (yes there are also pre-defined data structures that are dynamic arrays in C++ but that is beside the point).
Typically objects are passed by reference (but that may depend on the implementation). However, Smalltalk methods are treated as an in-going route (messages can send arguments) from which a value is returned: one way IN; one way OUT. C++ is a VERY different language from Smalltalk so I wouldn't advise you to try to think of Smalltalk OOP in C++ terms. C and C++ deal with memory, arrays, addresses and pointers at quite a low level whereas Smalltalk operates at a high level of abstraction. Just concentrate on the "methodology"of Smalltalk (how it treats objects and how it communicates using messages and methods) rather than trying to figure out its implementation details.
Your GetBonus example isn't quite right. The argument passed by reference is technically part of the function's interface, so if the developer doesn't modify the argument, he's violated the interface's contract. It's more of an example of why not to duplicate interface features as part of making good interfaces.
However you define it, the point I'm trying to make is that the internal implementation details may affect code outside the object - something that has the potential to cause side-effects that were not anticipated by the method's writer. Best wishes Huw
Typical of academia, which has polluted the business world is excessive jargon. Essentially,, it is Tower of Babel gibberish. Egg heads misunderstand each other and hence the proliferation of false meanings to concepts. Often they are not even talking about the same levels of zoom. The confuse and conflate all of these: modularity, modules, modular programming, information hiding, encapsulation, object-oriented, message oriented. *RIGHTLY* Modular programming is how one implements modular design, i.e., modularity. A module should be a component of a system emulated or modeled in software. In modular programming, Team A could write a module emulating a printer. So could Team B. The internals are irrelevant. What matters is that each module exports the same interface so that Team C can use either version. *ALL OF THE JARGON and CONTINUAL JARGONIZING* reveals what utter bullshit the field of computer science is, at least on the software side. In software, the closest, truest concept of modularity are plug-ins / add-ins. Some would argue dynamic linked libraries, but these only offer up functions. Take a real world object like a guitar amp. It has internal electronics that manipulate electronic signal. It has an interface to control volume, signal frequencies and so on. A software guitar amp modeller would be a modular system that would abstract the UI and the amp internals so that one could model write "black box code" to produce the signals of these amps: Fender 5E3 Deluxe, Marshall JCM800, Marshall 1959 Super Lead 100 Watt Plexi, Vox AC30, Fender Twin Reverb and so on. All that would be needed is an API to tweak the generated signal with software controls that emulate volume, treble, bass, reverb, etc. *REAL OOP* Real OOP would use DSLs to pass messages and not function calls renamed as method calls. "Tell me your size" would be the message rather than a.sizeof *Another notably bad misuse of language by eggheads is "information hiding".* Rightly that ought to be called DESIGN HIDING or IMPLEMENTATION HIDING or at minimum INTERNAL STATE DATA HIDING. Information is data in sequence that conveys meaning. Key-value pairs might define aspects of a thing, but are not information. Code to implement a method or function definitely is not information.
This videos required so much research that the hard work shines on your face! Mad respect!
Shines on my face? 😂 I'll try to hide that in future. Seriously though, many thanks!
I’m loving this series. I appreciate your clear and straight forward way of teaching. It’d be wonderful to see a video covering the functional programming approach as well.
Thank you. Maybe later on.
Nice explainer.
As a college student learning modularity (yay Pascal!) in the early-to-mid 80s, this idea was not taught. I'm not sure object-oriented programming was even mentioned. When I came across OOP after college, I could not wrap my head around it. When I dove headlong into PL/SQL (and O'Reilly's PL/SQL Vol 2) is when I really started getting a better grasp. And still, I'm barely a novice.
Thanks, Jack. I teach a number of courses online these days and it took me a while to realise how many students really don't know what modularity (or encapsulation) are all about. At least, I hope this will give people some things to think about.
I'm a beginner Python developer and I have wrote code in procedural way. I was wondering why we need OOP. I love how this series explains what are the urgencies and problem that OOP mean to solve. You are a great explainer and storyteller too!
Many thanks. It's worth being comfortable with both procedural and OOP style. Have fun!
Very interesting aspects of OOP. I think what modern languages do is a totally broken OOP. It is some "pragmatical" mixture of the original ideas. It can not even be fixed because of the ecosystem which forces to "inherit" the wrong behavior. People often think OOP modularity is about using private fields with public methods but that is not true. OOP modularity is a different way of thinking. You can write low level accessors and mutators for private fields that seems to be very modular, however it is just a fake modularity with low level binding. Message sending is about high level binding: I ask/request the printer to print not just set its inner values using setters then call its print method. Etc..etc...
Low level binding is bad because it leaks the inner structure of an object and leaks cause spaghetti code and reduces robustness.
Lovely explanation. Thank you
Many thanks.
Thank you Huw for giving me a much better understanding of OOP. Never has it been explained so well to me👍
That's very kind of you to say so. Many thanks!
Thanks for this course! I feel less alone finally. ❤
As a Smalltalk developer I had to work on other projects in other languages (C#, Python, Java) and it was always a big pain as most of developers don't understand OO, don't understand encapsulation, don't understand messaging and therefore modularity. In addition they think that OO is mainly about state and state change which is not at all a prerequisite for OO.
I think all programmers would learn something from using Smalltalk, even if they never use it for any serious projects. Anyway, many thanks for the comment.
I guess from the example you gave here is why some are advocating a functional programming paradigm.
I really liked this explanation!
Thank you, David.
me too
Thankyou for this in depth exploration of object-oriented programming. I am getting hung up on this idea of message passing. In an earlier video I believe you mentioned that a message may not be a command. This gives me the impression that messages should behave more like events. So I wonder if part of the confusion with OO is just this. For example, rather than telling the black box to give you chocolate, you might just say I am hungry for chocolate.
The essential idea is that it is up to each object to "decide" how to respond to a message. The methods declare a route of communication with an object. The messages make requests to be answered by the methods. This sounds odd at first but it is really an approach to modularity. You may find it easier to think of methods as the declared "interfaces".
This is in line with how modularity and encapsulation are understood in cognitive science. Perhaps it’s no accident Fodor wrote “Modularity of Mind” in the early 80’s
I didn't know that. What an interesting thought!
This channel is vastly under appreciated by the algorithm. Hey Algorithm, GET IT TOGETHER.
Many thanks!
Very well explained
Many thanks.
I will write my first assumption of what modularity is here and see if by the end of this video, the explanation is better.
Modularity is simply the ability to hold one module in the mind’s context without having to consider anything else.
Modularity is not a computer science concept. It is a reality concept.
All of the ideas around it exist to accomplish this.
Information hiding isn’t anything really. It is simply a way to control what users (including me) have to think about when looking at this block.
Imagine if all clocks were opened up and you had to solder the power source to it to start it.
This is the other extreme against modularity.
It works, but the amount of irrelevant information provided to the consumer can be greatly reduced.
I have personally found the physical analogy to be most helpful in understanding all of the techniques around it.
Now, to watch the video.
Does it matter whether the object in question is a black box or a white box regarding modularity though? I mean if an object only holds data, what's wrong with exposing the data it holds directly. For example, in C#:
```
public enum MidiNote : byte
{
A1,
ASharp1,
B1,
C1
//...↓
}
public readonly record struct PianoKey(decimal Frequency, MidiNote MidiNote);
public readonly record struct PianoKeyboard(IEnumerable Keys);
```
All of the data on all of these objects can come out of the object the same way it went in; I guess the question comes in on: where does that data get created? In this form I'd say I'd have a function on PianoKeyboard which creates a default set of PianoKeys but by also having this constructor public, it allows an alternative implementation to be created by the caller - does this break modularity though?
If you only ever need to assign or retrieve data items, the use of private data and public accessors may seem like overkill. However, if a class's author later decides to modify the data before returning it (say a string is auto-capitalised or some tax rate is calculated) then the fact that the data is hidden makes a big difference. If a programmer outside the object relies on some data item being in a given state (e.g. a string that is lowercsae or a value having a specific tax rate) then that code will now contain errors by accessing the data item direct. But code that goes via a method will be correct because it automatically gets the changes made by the implementor of those methods. In general, there is no downside to hiding data (unless, in very time-dependent code, a programmer wants data access optimised for speed) but there might be a downside if a programmer bypasses methods since any modified implementation details will also be bypassed.
Best wishes
Huw
@@LearnWithHuw Thanks Huw - I have encountered this in the past where I had public fields and I wanted to raise a ValueChanging and a ValueChanged event before and after each one was set. However, with the `record` example, the values are essentially held as immutable properties - this means the underlying implementation could be changed to give these backing fields and even make them mutable without having to change anything on the caller's end. Regarding your example though about upper-casing the first letter or applying a tax rate, I'd argue that if the object is immutable, this should have been done upon construction of the object (when the value is passed-in) - whilst there would be an initial upfront performance hit of doing this (when the object is created), it would mean that the same data needn't be recalculated each time it needs to be retrieved.
Modular programming thrives on the concept of "loose coupling," keeping components independent and interactions minimal. I'm not sure why Smalltalk wraps it up in a layer of mystery-it's really just about making code easier to manage and evolve.
I think the jargon seems to make it more mysterious. When Smalltalk emerged, the assumption was that programmers would be unable to understand anything in which procedures were "unbound". So the idea of passing messages to objects was introduced as a way of "simplifying" the notion of communication with procedures via their containing objects. Over time, the jargon has become more of a hindrance than a help, however.
These videos feel like Cosmos from Sagan but for programmers 🙌
Yikes! That's a lot to try to live up to! 😅
Where exceptions fit into this? for example divide by zero exception? Is it considered as a response?
Good question. Difficult to answer. In principle, divide by zero (e.g. 3 / 0) is a message that sends the object 0 to the integer 3 and it can then respond appropriately, as with any other message. In practice, some exceptions might not be so easily predictable and trappable. I think modern Smalltalk implementations have various different exception-handling capabilities. There is an interesting discussion of that here: wiki.c2.com/?SmalltalkAndExceptions
I'll need to look into this more fully and maybe do separate video on the subject later on. The whole history of exception handling is probably worth looking into, in fact!
8:16 isn't this just the functional concept of pure vs impure functions?
Technically this is a breach of contract since the caller is relying on unspecified internal behavior (the object being passed being altered internally) but I see your point.
No bad though. So far I've only seen a couple of your videos and although I find them a bit drawn out, that might be a good thing for those not so familiar with what you should _actually_ be doing with OOP, which seems to be a lot of programmers. I had a suspicions that the original ideas were actually well thought out but had their various implementations broken (cough, C++, cough) and it looks like those were right.
Yeah the change in num is part of the method's specification. Changing that is not changing the "internals" of the object, it's a change to the API. The same programmer could've messed up the return value as well, so you won't be the least bit safer by relying on that. I still prefer return values over inout arguments but that's just because the caller side looks more explicit.
So let me get this straight : You can only pass by value into a function of an object in Smalltalk?
How about passing arrays of any dimensions? In C++ that is done by reference since you wouldn't get to the "next value" in the index of the array without knowing where the whole array starts.
How is that done in Smalltalk? Or passing arrays as pointers where you can have dynamically sized arrays? (yes there are also pre-defined data structures that are dynamic arrays in C++ but that is beside the point).
Typically objects are passed by reference (but that may depend on the implementation). However, Smalltalk methods are treated as an in-going route (messages can send arguments) from which a value is returned: one way IN; one way OUT.
C++ is a VERY different language from Smalltalk so I wouldn't advise you to try to think of Smalltalk OOP in C++ terms. C and C++ deal with memory, arrays, addresses and pointers at quite a low level whereas Smalltalk operates at a high level of abstraction. Just concentrate on the "methodology"of Smalltalk (how it treats objects and how it communicates using messages and methods) rather than trying to figure out its implementation details.
Your GetBonus example isn't quite right. The argument passed by reference is technically part of the function's interface, so if the developer doesn't modify the argument, he's violated the interface's contract. It's more of an example of why not to duplicate interface features as part of making good interfaces.
However you define it, the point I'm trying to make is that the internal implementation details may affect code outside the object - something that has the potential to cause side-effects that were not anticipated by the method's writer.
Best wishes
Huw
😮😮😮😮😮😮😮😮
😮😮😮😮😊😮😮
Typical of academia, which has polluted the business world is excessive jargon. Essentially,, it is Tower of Babel gibberish.
Egg heads misunderstand each other and hence the proliferation of false meanings to concepts.
Often they are not even talking about the same levels of zoom. The confuse and conflate all of these: modularity, modules, modular programming, information hiding, encapsulation, object-oriented, message oriented.
*RIGHTLY*
Modular programming is how one implements modular design, i.e., modularity.
A module should be a component of a system emulated or modeled in software.
In modular programming, Team A could write a module emulating a printer. So could Team B.
The internals are irrelevant. What matters is that each module exports the same interface so that Team C can use either version.
*ALL OF THE JARGON and CONTINUAL JARGONIZING* reveals what utter bullshit the field of computer science is, at least on the software side.
In software, the closest, truest concept of modularity are plug-ins / add-ins. Some would argue dynamic linked libraries, but these only offer up functions.
Take a real world object like a guitar amp. It has internal electronics that manipulate electronic signal. It has an interface to control volume, signal frequencies and so on.
A software guitar amp modeller would be a modular system that would abstract the UI and the amp internals so that one could model write "black box code" to produce the signals of these amps: Fender 5E3 Deluxe, Marshall JCM800, Marshall 1959 Super Lead 100 Watt Plexi, Vox AC30, Fender Twin Reverb and so on.
All that would be needed is an API to tweak the generated signal with software controls that emulate volume, treble, bass, reverb, etc.
*REAL OOP*
Real OOP would use DSLs to pass messages and not function calls renamed as method calls.
"Tell me your size" would be the message rather than a.sizeof
*Another notably bad misuse of language by eggheads is "information hiding".*
Rightly that ought to be called DESIGN HIDING or IMPLEMENTATION HIDING or at minimum INTERNAL STATE DATA HIDING.
Information is data in sequence that conveys meaning. Key-value pairs might define aspects of a thing, but are not information. Code to implement a method or function definitely is not information.