I think that's a pretty large generalization. How tightly coupled objects are is not an inherent part of OOP, it depends on the design. In fact, the component model is designed to reduce couplings between objects by requiring objects to communicate only through well-defined interfaces.jbanes wrote:OOP had its day. While it helped everyone better structure their thinking, it's not a good concept to structure a modern system around. The couplings are too strong for the system to hold up under any level of complexity.
Unless you like visiting endless amounts of pain on engineers, that is. In that case go right ahead.
Everything is an Object
- Marionumber1
- Member
- Posts: 56
- Joined: Sun May 08, 2011 9:03 am
Re: Everything is an Object
Programmer and security enthusiast
DarkSide OS Kernel
Those who do not understand Windows NT are doomed to criticize it, poorly.
DarkSide OS Kernel
Those who do not understand Windows NT are doomed to criticize it, poorly.
Re: Everything is an Object
One of the key (unsolvable) issues with "true OOP" is that classes are static things. Once you define their interface, they can't change. This usually isn't too bad at runtime. But the development cost, oh the development cost! I did some calculations on it once, and realized that 60%-80% of my engineers' time was spent on Object Mapping nonsense. (ORMs are an especially bad idea.)Marionumber1 wrote:I think that's a pretty large generalization. How tightly coupled objects are is not an inherent part of OOP, it depends on the design. In fact, the component model is designed to reduce couplings between objects by requiring objects to communicate only through well-defined interfaces.
If you take a step back, you realize that there is no fundamental difference between a Class and a Map. The only difference is that Classes are static.
But why are they static? No real reason. It just happened to be the easiest implementation when OOP was originally designed. Thus static classes became codified into the concept.
As it is wont to do, software complexity is growing far past what can be effectively maintained by hand in a class-based system. Maintaining classes has become a boring, meaningless task with no real benefit. Which means that we should make a computer do it.
We could use a code generation system, but those are fragile and not responsive at runtime. Instead, consider using meta-programming techniques. Let the data dynamically drive the structures. You can recognize similar things through duck typing rather than static typing.
This is all very theoretical at this point, so let me try and spell it out in a simpler to understand manner.
Given the layout you provided, the following 23 classes must exist. (At a minimum!)
Clear relationships must be maintained between these to ensure the static correctness of everything. (See the coupling creeping in?)Audio, MP3, WAV, WMA, Image, BMP, JPG, PNG, System, Graphics, Canvas, Window, IO, File, Section, IPC, Queue, Pipe, Socket, Semaphore, Mutex, RWLock, Timer
You might try to improve things by creating abstract classes or interfaces like these:
Now you're up to 26 classes. But suddenly you realize that some of the things you support have radically different features! PNGs can be animated, BMPs and PNGs have paletized versions, JPGs are lossy, and tons of other info you might want to expose. Since you don't want programs to have to code to specific formats, you start adding more abstract classes/interfaces to expose this additional typing:AudioFormat, ImageFormat, DrawSurface, etc.
We're up to 32 classes and we're just getting started on this simple example! And none of these classes actually DO any work. They just represent things. You still need those nasty, non-OOP files that perform functions like loading and saving and dealing with dirty surfaces, etc. Before long you're screaming in agony, "WHY WON'T YOU LINE UP SO PRETTY LIKE YOU DID IN MY DIAGRAM!?!?!"LossyAudioFormat, DRMAudioFormat, AnimatedImageFormat, PaletizedImageFormat, LossyImageFormat, DecoratedDrawSurface
Yes, that actually happens.
If you start thinking in terms of Maps and Lists, many of these problems get much simpler to solve. Your pretty looking hierarchy can stick around, but will become a dynamic structure that running code can plug into. I don't know if you can read JSON/Javascript, but here is an example representation:
Code: Select all
{
"Audio": [
"MP3": {
lossy: true,
getPCM: function() {...},
play: function(PCMDevice) {...},
bitrate: ...
},
"WAV": {
getPCM: function() {...},
play: function(PCMDevice) {...},
sampleRate: ...
},
"WMA": {
drm: true,
addDRMKey: function(key) {},
getPCM: function() {...},
play: function(PCMDevice) {...}
}
],
"Image": [
"BMP": {...},
"JPG": {...},
"PNG": {...}
],
"System": [
"Graphics": [
"Canvas": {...},
"Window": {...}
],
"IO": [
"File": {...}
"Section": {...}
],
"IPC": [
"Queue": {...},
"Pipe": {...},
"Socket": {...},
"Semaphore": {...},
"Mutex": {...},
"RWLock": {...},
"Timer": {...}
]
]
}
As a bonus, we've made enumeration of things super-easy. Which makes meta-programming far more possible.
Now here is where things get cool. Let's say that we have code to handle video and code to handle audio. Well it's definitely not hard to pipe audio and video out to the appropriate devices. We can test the objects for appropriate function support and pass them on in. But this also means that strange things can take place. For example, a GIF image could be set to an audio track because the system doesn't attempt to differentiate between an animated image and a video. Or we can pipe outputs from other programs (e.g. a video game) as stand-ins for the music or video that we're encoding.
In fact, a lot of inter-data type of stuff gets simplified. Code that does translations between things will look for duck-typed interfaces and use them if they exist.
Soon you'll find places where code is repetitive (and it will be at the function level, not the object level) and start optimizing code away. Thus growth in your system becomes a matter of constant re-factoring. Code sizes and accidental complexity stay low. Functionality and desired complexity skyrocket.
I realize it doesn't have the same intuitive appeal that "clean" OOP architectures have. But you need to remember that OOP architectures are only clean on paper. No plan survives contact with the enemy^H^H^H^H^H customer. And OOP systems have a horrible track record of bloat once they hit the real world.
If you don't believe me, look at any real-world Java server. Or even worse, Microsoft Windows APIs. (Ugh.) They just grow out of control. And worse yet, they keep getting replaced with "newer" and "better" interfaces. Because the old ones can't adapt.
The system with the flexibility to adapt and change will win. That's why next-gen functional designs are the way to go and not OOP.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: Everything is an Object
Congratulations, you've just gone and invented a worse version of COM (Worse because it'll (A) be slower and (B) be less interoperable).
You've made the fundamental mistake - that far too many people have - that OOP is about classes, when it is about traits (or interfaces, but trait is probably a better word).
You see, no application really cares about the fact that the image its' dealing with is a PNG, it just cares about the fact that its' an image; perhaps animated. Maybe it has some metadata as well.
You say that Win32 development is a continuous cycle of technology replacement, but thats' wrong. Its' not Windows which has had the continuous technology replacement; its' the libraries which come from Microsoft's developer division which have. The core object portion of the Win32 API - that is, COM, - has remained fundamentally the same since Windows 3.1, modulo the changes threading introduced in Win95 and those introduced by DCOM. Its' notable that it has maintained compatibility throughout this.
And yes, its very true that COM gets a bad rap. Some of this is down to the fact that it has some very abstract concepts (Monikers) which are pretty much abysmally explained everywhere besides' Don Box's Essential COM book, and some of it is down to the fact that C and C++ development for COM is truly painful. Microsoft has made various attempts to simplify this over the years, but have suffered that they've now got three somewhat mutually incompatible libraries (ATL, MFC, WTL, again, DevDiv technology churn) and all of them can trace their history back to Visual C++ 6 which, well, had limited support for the kind of advanced C++ functionality needed to truly paper over the rough edges. Hell, even today they would have issues fixing this without help from MIDL (the IDL compiler). If you look at what Microsoft have been doing with WinRT and in particularly C++/CX, you'll see that they've just taken COM, replaced the type libraries with the .net metadata format (more rich, more interoperable with .net), and extended the compiler so that it understands that metadata and can itself directly paper over the rough edges of COM (things like reference counting, needed to approximate cross runtime cross system GC).
When it comes down to it, once you stop thinking in terms of Classes and start thinking in terms of Traits, OOP, and particularly component oriented programming start to make a lot of sense. Traits are just a description of what something is, expressed in a machine readable form. That's basically what any API boils down to.
But so far we have only been discussing what I call "First order" component oriented systems. They're great! Loads of Windows apps are built out of them. OLE, the origin of COM, is perhaps its' biggest testament: 20 years on from Windows 3.1 and outside of Windows I still can't embed, say, a webpage inside a text document,
Magic happens, however, when you hit the second order component systems. Rust seems to be approximating this with its' implementation of Traits (Seriously, have a look at Rust. Its' a systems programming language designed by people who actually understand modern C++ - in other words, its' perhaps the first serious attempt at a C++ replacement which actually has the ability to replace it), but my first interaction with one came from the 'runt' of the Python world: Zope.
The Zope guys had built their web application framework, then Zope version 2, and it was powerful, but they'd discovered that it was becoming a tightly coupled monolithic ball. They set about to reinvent it, something more loosely coupled. They started by creating an interface system so that they could build contracts for classes to stick to, and a simple registry of factories.
What they discovered was that their code was starting to accumulate "x is a Y" checks, and that classes were starting to accrete implementations of interfaces which, truthfully, were fairly unrelated to their core purpose. Someone had a think about this and how to solve it, and they came upon a simple and elegant solution: adapters.
Its' a pretty simple concept: You just ask the component system* "Take this object I've got and give me an implementation of IFoo from it". The object you handed it might implement IFoo directly, or somebody might have defined an adapter FooFromBar which adapts IBars to IFoos.
Magic happened. You got rid of the spaghetti layering that traditional OOP tends to encourage. Great bits of functionality got decoupled from each other.
As an example, say you need to get an icon for an arbitrary object. You'd define a new interface "IIcon" which returns it. You might then implement
I'm not going to say its' perfect; of course not. There is a reason Zope is the [or a] runt of the Python world. A lot of that is that backwards compatibility means that what was Zope 2 is never really going away, and the other part is that Zope 3 succumbed somewhat to the second system effect. There are some people doing cool stuff based upon what came out of it; Launchpad is built on top of it, for example. But, unfortunately, perhaps because they were coming from a system which was spaghetti code, the developers ended up creating Ravioli Code. It doesn't help that documentation is at best spotty and often missing.
But, returning to my core point: Its' not about classes, it's about traits or interfaces. That gets you to a first order component system. Magic happens in the second order, where you get the ability to add extra functionality to classes at run time.
Its' kind of the confluence of interfaces and duck typing: Its' not just "if it quacks like a duck", but also "if anybody can make it quack like a duck". And you get back your interface contract checking at compile time, too, if you want it.
(* I simplified matters slightly; Zope adapters are more powerful. In specifics, they can adapt multiple objects into a new object. If you want further detail, go and read the zope.component documentation)
You've made the fundamental mistake - that far too many people have - that OOP is about classes, when it is about traits (or interfaces, but trait is probably a better word).
You see, no application really cares about the fact that the image its' dealing with is a PNG, it just cares about the fact that its' an image; perhaps animated. Maybe it has some metadata as well.
You say that Win32 development is a continuous cycle of technology replacement, but thats' wrong. Its' not Windows which has had the continuous technology replacement; its' the libraries which come from Microsoft's developer division which have. The core object portion of the Win32 API - that is, COM, - has remained fundamentally the same since Windows 3.1, modulo the changes threading introduced in Win95 and those introduced by DCOM. Its' notable that it has maintained compatibility throughout this.
And yes, its very true that COM gets a bad rap. Some of this is down to the fact that it has some very abstract concepts (Monikers) which are pretty much abysmally explained everywhere besides' Don Box's Essential COM book, and some of it is down to the fact that C and C++ development for COM is truly painful. Microsoft has made various attempts to simplify this over the years, but have suffered that they've now got three somewhat mutually incompatible libraries (ATL, MFC, WTL, again, DevDiv technology churn) and all of them can trace their history back to Visual C++ 6 which, well, had limited support for the kind of advanced C++ functionality needed to truly paper over the rough edges. Hell, even today they would have issues fixing this without help from MIDL (the IDL compiler). If you look at what Microsoft have been doing with WinRT and in particularly C++/CX, you'll see that they've just taken COM, replaced the type libraries with the .net metadata format (more rich, more interoperable with .net), and extended the compiler so that it understands that metadata and can itself directly paper over the rough edges of COM (things like reference counting, needed to approximate cross runtime cross system GC).
When it comes down to it, once you stop thinking in terms of Classes and start thinking in terms of Traits, OOP, and particularly component oriented programming start to make a lot of sense. Traits are just a description of what something is, expressed in a machine readable form. That's basically what any API boils down to.
But so far we have only been discussing what I call "First order" component oriented systems. They're great! Loads of Windows apps are built out of them. OLE, the origin of COM, is perhaps its' biggest testament: 20 years on from Windows 3.1 and outside of Windows I still can't embed, say, a webpage inside a text document,
Magic happens, however, when you hit the second order component systems. Rust seems to be approximating this with its' implementation of Traits (Seriously, have a look at Rust. Its' a systems programming language designed by people who actually understand modern C++ - in other words, its' perhaps the first serious attempt at a C++ replacement which actually has the ability to replace it), but my first interaction with one came from the 'runt' of the Python world: Zope.
The Zope guys had built their web application framework, then Zope version 2, and it was powerful, but they'd discovered that it was becoming a tightly coupled monolithic ball. They set about to reinvent it, something more loosely coupled. They started by creating an interface system so that they could build contracts for classes to stick to, and a simple registry of factories.
What they discovered was that their code was starting to accumulate "x is a Y" checks, and that classes were starting to accrete implementations of interfaces which, truthfully, were fairly unrelated to their core purpose. Someone had a think about this and how to solve it, and they came upon a simple and elegant solution: adapters.
Its' a pretty simple concept: You just ask the component system* "Take this object I've got and give me an implementation of IFoo from it". The object you handed it might implement IFoo directly, or somebody might have defined an adapter FooFromBar which adapts IBars to IFoos.
Magic happened. You got rid of the spaghetti layering that traditional OOP tends to encourage. Great bits of functionality got decoupled from each other.
As an example, say you need to get an icon for an arbitrary object. You'd define a new interface "IIcon" which returns it. You might then implement
- An adapter from IDirectory, which shows a directory icon
- An adapter from IFile, which shows an icon guessed by MIME type, or file extension, or magic numbers
- An adapter from Interface (effectively zope.interface's root type), which shows a generic icon
I'm not going to say its' perfect; of course not. There is a reason Zope is the [or a] runt of the Python world. A lot of that is that backwards compatibility means that what was Zope 2 is never really going away, and the other part is that Zope 3 succumbed somewhat to the second system effect. There are some people doing cool stuff based upon what came out of it; Launchpad is built on top of it, for example. But, unfortunately, perhaps because they were coming from a system which was spaghetti code, the developers ended up creating Ravioli Code. It doesn't help that documentation is at best spotty and often missing.
But, returning to my core point: Its' not about classes, it's about traits or interfaces. That gets you to a first order component system. Magic happens in the second order, where you get the ability to add extra functionality to classes at run time.
Its' kind of the confluence of interfaces and duck typing: Its' not just "if it quacks like a duck", but also "if anybody can make it quack like a duck". And you get back your interface contract checking at compile time, too, if you want it.
(* I simplified matters slightly; Zope adapters are more powerful. In specifics, they can adapt multiple objects into a new object. If you want further detail, go and read the zope.component documentation)
Re: Everything is an Object
Traits are not an OOP concept. What you're describing is very close to duck typing, just with needless attempts at typing involved. What you want is a modern functional language. It will do what you want. It can also be appropriately simulated in an OOP language, but you can't use language features to accomplish it. Interfaces just lead to more coupling.Owen wrote:Congratulations, you've just gone and invented a worse version of COM (Worse because it'll (A) be slower and (B) be less interoperable).
You've made the fundamental mistake - that far too many people have - that OOP is about classes, when it is about traits (or interfaces, but trait is probably a better word).
Yes, I see your point.Owen wrote:You say that Win32 development is a continuous cycle of technology replacement, but thats' wrong. [...] Microsoft has made various attempts to simplify this over the years, but have suffered that they've now got three somewhat mutually incompatible libraries (ATL, MFC, WTL, again, DevDiv technology churn)
IDL? Well why didn't you say so? Sign. Me. Up.Hell, even today they would have issues fixing this without help from MIDL (the IDL compiler).
I can't wait to spend needless hours writing useless mappings for objects that that already have definitions, just so we can make it language agnostic! While we're at it, can we throw some WSDL in for good fun? We can auto-generate those, but then everyone will switch to taking a single XML parameter as a value so that their interface can be upgraded without breaking the contract!
...
Apologies for the sarcasm, but it really is that bad. What you're describing is not the next generation of technology, but pure hell. You're setting yourself up for exactly the sort of couplings that will get you in trouble.
Worse yet, you're arguing the problem is that the technology just isn't sophisticated enough. This argument is very common in architecture circles and basically boils down to: "If I add enough complexity, it will eventually become simple!"
The problem with that line of thinking is that the laws of physics are not on your side. We engineers like to think of ourselves as "fighting entropy", but that's just not how entropy works. The truth is that our efforts create even more entropy. That's a basic fact of the universe. Thus it behooves us to think in terms of managing complexity instead of fighting disorder.
If you look at it from that perspective, it becomes rather obvious that the vast majority of ideas we have for typing and contracts fall under the heading of "accidental complexity". i.e. Complexity that is undesirable because it complicates the system in ways that are not useful to the end goal.
To roll it into a few simple platitudes: Keep It Simple and Stupid, Perfect is the enemy of good, Good enough is the enemy of great, and Worse is Better.
That's why "bad" systems generally seem to win, but occasionally something pretty special pops up despite some sort of "badness" behind it. (iOS being a perfect example. Who thought ObjC was a good language?!?)
-
- Member
- Posts: 283
- Joined: Mon Jan 03, 2011 6:58 pm
Re: Everything is an Object
Howdy,
I feel the need to respond to this thread (finally). This is a discussion I have constantly (Functional vs. Object Oriented Design) had with coworkers/fellow IRC users/personal friends/"random people".
I have worked (thoroughly) with various programming languages such as PHP, C#, Java, JS, and technologies (read VMs in this case) such as Apache, IIS, Browsers, etc., and continue to do so. These language are considered to be, from not dynamic (C# and Java, which is flat out wrong in the case of C# if not Java as well) to very dynamic (JS is the most "dynamic" language I have yet to encounter.)
I have also worked with other languages such as C, C++, VB5-6, VB.Net, Basic, Fortran (vaguely), asm, Python, and Perl. (I'm sure I am missing some...)
The basics of this conversation boil down to where you want to see an error.
If you think the above statement is wrong, quote it, and give a specific example!
Outside of that... This discussion is a trade off of flexibility vs reliability and/or maintainability. Now, given this forum is about writing our own OS, and in the long run about writing our own User Space, let us, for the sake of comparability ignore documentation (or lack there-of).
Let us take a quick look of opinions from a developer's POV...
It's pretty clear you care less about reliability than you do Cost of Development. As a business owner, this makes sense, as a developer, a user, or an investor (that cares about maintainability, ie. future profits/ROI)
In terms of a class being "static", that is either a bonus, or a determinate. Most career developers would likely say that it is a bonus. (As the static verification makes their life easier in the long run)
... To be continued in a future message tomorrow ...
- Monk
P.S. I *HAVE* been drinking. At the same time, *I* can see how ludicrous this discussion has become at certain points...
I feel the need to respond to this thread (finally). This is a discussion I have constantly (Functional vs. Object Oriented Design) had with coworkers/fellow IRC users/personal friends/"random people".
I have worked (thoroughly) with various programming languages such as PHP, C#, Java, JS, and technologies (read VMs in this case) such as Apache, IIS, Browsers, etc., and continue to do so. These language are considered to be, from not dynamic (C# and Java, which is flat out wrong in the case of C# if not Java as well) to very dynamic (JS is the most "dynamic" language I have yet to encounter.)
I have also worked with other languages such as C, C++, VB5-6, VB.Net, Basic, Fortran (vaguely), asm, Python, and Perl. (I'm sure I am missing some...)
The basics of this conversation boil down to where you want to see an error.
If you think the above statement is wrong, quote it, and give a specific example!
Outside of that... This discussion is a trade off of flexibility vs reliability and/or maintainability. Now, given this forum is about writing our own OS, and in the long run about writing our own User Space, let us, for the sake of comparability ignore documentation (or lack there-of).
Let us take a quick look of opinions from a developer's POV...
Last I checked, I only need to replace the "interface" and the rest of *MY* code fails to compile until i replace said Interface-defined-methods/properties.jbanes wrote:One of the key (unsolvable) issues with "true OOP" is that classes are static things. Once you define their interface, they can't change. This usually isn't too bad at runtime. But the development cost, oh the development cost! I did some calculations on it once, and realized that 60%-80% of my engineers' time was spent on Object Mapping nonsense. (ORMs are an especially bad idea.)
It's pretty clear you care less about reliability than you do Cost of Development. As a business owner, this makes sense, as a developer, a user, or an investor (that cares about maintainability, ie. future profits/ROI)
In terms of a class being "static", that is either a bonus, or a determinate. Most career developers would likely say that it is a bonus. (As the static verification makes their life easier in the long run)
The main difference to a developer is where "I" get feedback about an error. Is it on compile, or on execution? (I prefer compile, since I am a realist, and not an idealist...)jbanes wrote:If you take a step back, you realize that there is no fundamental difference between a Class and a Map. The only difference is that Classes are static.
Proof requested. (--Snip-- Please cite verifiable sources when you make such a wild claim...)jbanes wrote:But why are they static? No real reason. It just happened to be the easiest implementation when OOP was originally designed. Thus static classes became codified into the concept.
I actually agree with the second part of this statement. I have realized that based on (MY) ASP.Net Framework (or my PHP Framework which is incredibly similar) that "99%" of my customer's concerns/requests are stupid simple.... Then I realize that that last 1% will take 40+ hours and a small bit of customization...jbanes wrote:As it is wont to do, software complexity is growing far past what can be effectively maintained by hand in a class-based system. Maintaining classes has become a boring, meaningless task with no real benefit. ...
When you can deterministically generate all possible optimizations/possibilities for a given set of code without any developer input... Go join the GCC team.jbanes wrote:... Which means that we should make a computer do it.
Proof requested for the first part of this statement, AS WELL AS the second.jbanes wrote:We could use a code generation system, but those are fragile and not responsive at runtime.
Data driven structures are great... Where does functionality come in?jbanes wrote:Let the data dynamically drive the structures.
Correct. Which is better? (And define better in terms of performance vs. maintainability vs. some random thing functional programmers use as an argument? {lack of knowledge on my part, not an insult...})jbanes wrote:You can recognize similar things through duck typing rather than static typing.
So you replace compile time static checks with run-time checks... that force devs to check their "own sh*t" (read *MY* static typed parameters...) with run-time (more-than-likely user-found) problems?jbanes wrote:...Random *Off the top of your head* assumptions/contrived examples...
So instead of one "if( o.GetType() == typeof(ISomething) )" I have to do 5 checks for 5 different functions? NO thanks...jbanes wrote:You'll notice that all structural classes have been removed.
The last programming language I used with dynamically typing support did the same thing with static type checking... (ie. compile time errors instead of edge case user "checking")jbanes wrote:These objects can happily self-register into this structure, making the system self-organizing.
Congrats, I can know iterate over *EVERY* device in the PC and........ print the name? Why the hell would anyone ever do this?!?!?! Iterating over every HD is one thing, but to show the user anything meaningful, I would only show them HDs. How would I do that with your super dynamic system?jbanes wrote:As a bonus, we've made enumeration of things super-easy. Which makes meta-programming far more possible.
So at compile time no one knows what will happen... but at run time!!! CRASH! Hmmm.... Why did it crash? Or we could require an Interface for Video, and one for Audio and not crash! User can be happy!jbanes wrote:Now here is where things get cool. Let's say that we have code to handle video and code to handle audio. Well it's definitely not hard to pipe audio and video out to the appropriate devices. We can test the objects for appropriate function support and pass them on in. But this also means that strange things can take place. For example, a GIF image could be set to an audio track because the system doesn't attempt to differentiate between an animated image and a video.
... To be continued in a future message tomorrow ...
- Monk
P.S. I *HAVE* been drinking. At the same time, *I* can see how ludicrous this discussion has become at certain points...
Last edited by FallenAvatar on Wed Jan 08, 2014 4:15 pm, edited 1 time in total.
Re: Everything is an Object
Can already imagine the post in the About this site asking why this thread was locked.tjmonk15 wrote:You are not smarter than any specific user here
I say opinions lead to butt hurt members, and butt hurt members lead to flame wars. This is an opinionated topic judging on the techniques developers chose. One guy will think its a great idea for what he does, the other will nit pick at his facts when its just opinion or experience that he chooses to do what he does.
But that's just my opinion, i usually just post B.S here.
-
- Member
- Posts: 283
- Joined: Mon Jan 03, 2011 6:58 pm
Re: Everything is an Object
That didn't quite come across as I meant. It has been removed.VolTeK wrote:Can already imagine the post in the About this site asking why this thread was locked.tjmonk15 wrote:...
And I agree, this is a very opinionated topic with now 100% correct answer (or an answer that is correct 100% of the time) It mainly is personal preference.
- Monk
Re: Everything is an Object
Hello!tjmonk15 wrote:Howdy
Before I begin my response, please keep one thing in mind: This is ultimately just my opinion. I'm giving my thoughts so that others may learn from them. Or not. It's up to you. Your kernel is your kernel, and I wish each developer the best in attempting to prove their own opinions. So please take it for what it is: an opinion. And as we know, everyone has one.
For what it's worth, I would expect no less from members of this forum. Developing an OS is not an amateur's game.I have worked (thoroughly) with various programming languages such as PHP, C#, Java, JS, and technologies (read VMs in this case) such as Apache, IIS, Browsers, etc., and continue to do so. These language are considered to be, from not dynamic (C# and Java, which is flat out wrong in the case of C# if not Java as well) to very dynamic (JS is the most "dynamic" language I have yet to encounter.)
I have also worked with other languages such as C, C++, VB5-6, VB.Net, Basic, Fortran (vaguely), asm, Python, and Perl. (I'm sure I am missing some...)
That's the classic argument against dynamic solutions. The problem is that there is no evidence that static compilation checking reduces bug rates. In perhaps the most famous work on this problem, Les Hatton's Software failures-follies and fallacies, the following conclusion was drawn:Last I checked, I only need to replace the "interface" and the rest of *MY* code fails to compile until i replace said Interface-defined-methods/properties.
A similar conclusion was reached in Steve McConnell's book "Code Complete", where code size was deemed the most important indicator of bugs. A very good discussion on the book and the specific issue of code size can be read at the following URL:We can conclude that programming language choice is at best weakly related to reliability.
http://mayerdan.com/ruby/2012/11/11/bug ... ode-ratio/
More specifically on the problem of Dynamic vs. Statis, the following paper was published by Microsoft researchers stated the following:
Interestingly, that paper argues that a mix of static and dynamic can produce the most reliable results. Which is something I tend to agree with. The underlying problem is that the amount of external data that a program must consume continues to grow. This data forces a program to handle the typing dynamically, which makes its static typing useless. Thus test cases must provide a stand-in solution. However, anywhere where your program is internally consistent, you can absolutely use typing to reduce errors.Static typing provides a false sense of safety, since it can only
prove the absence of certain errors statically [17]. Hence, even
if a program does not contain any static type-errors, this does
not imply that you will not get any unwanted runtime errors.
Unfortunately, that is a provable untrue statement. I did quite a bit of study on the topic over the last few years. What I found was that by switching to more functional and/or dynamic techniques (note that I did not say that it was always a dynamic language) I was able to reliably reduce code sizes by 90% in comparison to similar software being developed in parallel using traditional techniques. When the techniques were applied to the software developed in parallel (i.e. by replacing modules using the newer techniques) code sizes dropped by the expected 90%.It's pretty clear you care less about reliability than you do Cost of Development. As a business owner, this makes sense, as a developer, a user, or an investor (that cares about maintainability, ie. future profits/ROI)
As one might expect from such a drop in code sizes, reliability went up considerably. While I'm afraid I don't have the exact numbers available to me anymore (it was proprietary information) they were similarly shocking. Bug rates were between 60 - 90% less when compared between projects and/or previous versions. In fact, the conclusion reached was that the rates for the older techniques were so high that there were even more bugs being masked by the existing problems.
Again, I will stress that dynamic languages were only part of the solution. Our change was from server-side Java code using traditional MVC and object mapping techniques to Java and Javascript with the Java "speaking" dynamic maps and lists instead of objects. We also utilized a large number of anonymous inner classes to serve as replacements for the natural closure and lambda function passing you get in a true functional language.
The following blog post has been making the rounds among the Java and wider OOP community for a few years now:In terms of a class being "static", that is either a bonus, or a determinate. Most career developers would likely say that it is a bonus. (As the static verification makes their life easier in the long run)
http://steve-yegge.blogspot.com/2006/03 ... nouns.html
He does a fairly good job of explaining a perspective on the exact limitations of the class hierarchy. Class typing by itself isn't necessarily a bad thing (I used it to my advantage in making our dynamic Java more reliable by making sure highly pluggable Spring components could only fit together one way), but when used in a strict object model you end up with an inflexible system that cannot easily evolve, change, or even represent the numerous exceptions that occur due to leaky abstraction.
Basically it boils down to this: Translating a domain model to an object model is a really bad idea.
It's not that wild of a claim. If you trace through the history, it's easy enough to understand what happened. Simula 67 provided the first concept of classes, but it did not treat them as first-class citizens like later OOP systems like Java. Instead, it was a dynamic type that could be setup and utilized in a program without attempting to utilize it as a modular software solution. This made sense because the "Class" was attempting to model a real-world object with particular attributes.Proof requested. (--Snip-- Please cite verifiable sources when you make such a wild claim...)jbanes wrote:But why are they static? No real reason. It just happened to be the easiest implementation when OOP was originally designed. Thus static classes became codified into the concept.
This same concept was implemented in Common LISP with dynamic objects using a "Metaobject" syntax. This class-less design meant that objects could be constructed rather than pre-defined at compile time.
Smalltalk introduced the first language with truly first-class objects. In Smalltalk, everything was an object. Yet the type system was dynamic! This meant that Smalltalk shared a great deal with Simula, where the Object system existed to provide a mechanism for structured objects and not a unit of compilation and/or typing.
In fact, Smalltalk and Javascript share a great deal in common. Both languages are dynamically typed. In both languages, everything is an object. (Though Javascript quietly switches back and forth between primitives and objects as needed.) Both languages support the concept of duck-typing natively. (As opposed to Java's overweight reflection mechanism.) And both languages allow for dynamic object definition, with the primary caveat being Smalltalk requiring the class definition first while Javascript allows you to create the object dynamically. In that respect, Javascript ends up being somewhere between Smalltalk and Common LISP. Otherwise, Javascript is a "better" OOP language according to the Smalltalk "way" than Java is!
To actually get to this modern idea of statically defined "classes" as we think of them in Java and C#, we need to go to C++. When Stroustrup developed the language, he was trying to develop a way of bringing Simula-like classes to the C language. Thus rather than inlining definitions in a dynamic manner like Simula, he found a way to use C structures in combination with a pre-processor to define classes through a combination of a header file and a code file. These combined would utilize Classes as a modularization mechanism with extremely strong typing.
Java was influenced by this idea as Gosling started by trying to create a better C++ compiler. Eventually this morphed into Java. Source:
http://www.cs.dartmouth.edu/~mckeeman/c ... epaper.pdf
The rest, as they say, is history. The idea of a class as a static modularization solution took on a life of its own and gave us the modern world of domain models being translated into object hierarchies, which create difficult to manage code that is often less reliable than the non-OOP solutions.
As a final thought on the combined issue of reliability with dynamic code, I'd like to point out that some of the most reliable systems in the world run on Erlang. For those of who are familiar with Erlang know, it's used heavily in the telecommunications sector where system-wide failure simply isn't an option. If you're not familiar with the language, I highly recommend starting with Joe Armstrong's 2003 paper on the topic:
http://www.sics.se/~joe/thesis/armstron ... s_2003.pdf
Of course we can't. My point was not that we can predict everything ahead of time. My point was that as soon as you find yourself doing something repetitively, you should refactor to optimize it away. Using object-mapping code as the example, the problem was that programmers were spending all their time changing the Objects to match the latest iteration of the model. This made no sense as these objects were adding no value over the raw data structures. We "made the computer do it" in two stages. The first was moving to Maps and Lists. This dynamic structure enabled the second change, which was the ability to configure operations like common transformations (e.g. We could say "add key x and key y, output result to key z) rather than repetitively coding these operations to each object structure. This worked much better than the rats' nest of meta-typing needed to, for example, "generically" identify two arbitrary numbers as being capable of being added.When you can deterministically generate all possible optimizations/possibilities for a given set of code without any developer input... Go join the GCC team.jbanes wrote:... Which means that we should make a computer do it.
Defining these small pieces of highly reusable functionality means that the functionality can not only be driven through configuration, but also dynamically through runtime recognition. For example, in Javascript I've written filters that can recursively traverse a set and apply the filter logic anywhere in the hierarchy. Thus a simple definition like this:
exact match where [a, b, c] is 'Hi'
Can dynamically "match" the parent record for any of the following structures:
Code: Select all
{
a: {
b: {c: 'Hi'}
}
}
{
a: {
b: [{c: 'Bye'}, {c: 'Hi'}]
}
}
{
a: [
{b: [{c: 'Bye'}],
{b:[{c: 'Hi'}]}
]
}
Hopefully that addresses most of the concerns you raised and cites many of the specific arguments I'm attempting to make.
- Owen
- Member
- Posts: 1700
- Joined: Fri Jun 13, 2008 3:21 pm
- Location: Cambridge, United Kingdom
- Contact:
Re: Everything is an Object
Perhaps because the title of this thread is "Everything is an object", this seems to be some sort of appeal on your behalf against statically typed languages and interfaces everywhere
That's certainly not what I was trying to imply.
I agree with you, in the large. Java is terrible for modelling a lot of things. It makes people produce crazy things like AbstractProxyFactoryManagers.
But, given that this is an operating system development forum, were we not discussing a system interface? Let us not conflate that having a standardized component model for the system mean that all of your code need to internally work like it. That's not true at all.
And let us not conflate the rigidity of Java and co with that being necessary always. As I say, a lot of the design of my model comes from the Zope Component Architecture, a Python library.
My experience, and theirs, is that this is a highly productive environment. You get the advantages of duck typing, the ability to just walk through objects, etc, that dynamic typing gives you, and then you get the advantages usually conferred by static typing - the ability to verify that something really does what it says - with the advantage that anybody can provide an adapter that implements new behavior for that thing.
I've done web development in a variety of environments and languages. I've been through lots of different frameworks. In spite of its' documentation foibles, in spite of its' size... The Zope Toolkit and Grok are one of the most efficient I've ever used. A lot of effort that goes into extensibility in other frameworks is taken care of by the ZCA. It's marvelous, I just wish it had gained more traction...
Now, I don't expect to expose all of that flexibility in a system model. Encapsulation is important, when you're dealing with privilege and security boundaries in the interface, when you're doing IPC to a privileged server, or when you're doing RPC to a remote endpoint. Some flexibility must be sacrificed in the way of security (In other words: you won't just be able to iterate the members of an object at will)
But there is a lot to gain.
Oh, and before you mentioned your distaste for writing IDL:
That's certainly not what I was trying to imply.
I agree with you, in the large. Java is terrible for modelling a lot of things. It makes people produce crazy things like AbstractProxyFactoryManagers.
But, given that this is an operating system development forum, were we not discussing a system interface? Let us not conflate that having a standardized component model for the system mean that all of your code need to internally work like it. That's not true at all.
And let us not conflate the rigidity of Java and co with that being necessary always. As I say, a lot of the design of my model comes from the Zope Component Architecture, a Python library.
My experience, and theirs, is that this is a highly productive environment. You get the advantages of duck typing, the ability to just walk through objects, etc, that dynamic typing gives you, and then you get the advantages usually conferred by static typing - the ability to verify that something really does what it says - with the advantage that anybody can provide an adapter that implements new behavior for that thing.
I've done web development in a variety of environments and languages. I've been through lots of different frameworks. In spite of its' documentation foibles, in spite of its' size... The Zope Toolkit and Grok are one of the most efficient I've ever used. A lot of effort that goes into extensibility in other frameworks is taken care of by the ZCA. It's marvelous, I just wish it had gained more traction...
Now, I don't expect to expose all of that flexibility in a system model. Encapsulation is important, when you're dealing with privilege and security boundaries in the interface, when you're doing IPC to a privileged server, or when you're doing RPC to a remote endpoint. Some flexibility must be sacrificed in the way of security (In other words: you won't just be able to iterate the members of an object at will)
But there is a lot to gain.
Oh, and before you mentioned your distaste for writing IDL:
- You only need to do that if you're intending to build a component to expose to other people
- In those cases, you probably should have some interface contract anyway.
Re: Everything is an Object
I've spent a lot of time working on my first OS, and I've put a lot of thought into this issue, as Classes and Objects were at the top of my list of OS "features" I wanted to support. However, because there were so many disadvantages to this approach, I decided that perhaps the best approach would be to make them optional. My current approach is to implement this functionality in "layers", to allow the developer (currently, me) to choose what level of control/support he (I) would like.
Instead of hijacking your discussion with the details, I started a new post here: http://forum.osdev.org/viewtopic.php?f=15&t=27971
After reading through this thread, I feel like the duck-type adapter approach, along with some sort of class type definition would be the best solution.
Instead of hijacking your discussion with the details, I started a new post here: http://forum.osdev.org/viewtopic.php?f=15&t=27971
After reading through this thread, I feel like the duck-type adapter approach, along with some sort of class type definition would be the best solution.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
Re: Everything is an Object
jbanes
You have written really interesting post. But...
Is it really so complex?
You have written really interesting post. But...
Here is example how the search can be implemented in Java:jbanes wrote:exact match where [a, b, c] is 'Hi'
Can dynamically "match" the parent record for any of the following structures:
I can create such a filter in very few lines of a dynamic, functional language like Javascript. Trying to define a similar solution in Java has nearly given me an aneurysm.Code: Select all
{ a: { b: {c: 'Hi'} } } { a: { b: [{c: 'Bye'}, {c: 'Hi'}] } } { a: [ {b: [{c: 'Bye'}], {b:[{c: 'Hi'}]} ] }
Code: Select all
public static void recurse(Object x, List matches)
{
if (x==null) return;
Class c=x.getClass();
if (c.isPrimitive()) return;
if (c.isArray())
for (int i=0;i<Arrays.length(x);i++)
recurse(Arrays.get(i,x),matches);
else
for (Field f:c.getFields())
if (f.getType().equals(String.class) && "Hi".equals(f.get(x)))
matches.add(x);
else recurse(f.get(x),matches);
}
- AndrewAPrice
- Member
- Posts: 2299
- Joined: Mon Jun 05, 2006 11:00 pm
- Location: USA (and Australia)
Re: Everything is an Object
Nice argument but at the end what you describe is actually prototype-based programming which is an alternative way to implement the object oriented programming paradigm to class-based programming.jbanes wrote:That's why next-gen functional designs are the way to go and not OOP.
There's pros and cons of each. There's plenty of class vs. prototypes arguments online. My first experience of the power of prototype-based languages was when I was working with jQuery. I wanted a way to tag my data structure to the button, and I was Googling the API for some .tag property (and disappointingly I didn't find anything), and when thinking all hope was lost, I realized I could just assign a .tag property to any object dynamically.
It was an incredibly powerful feeling doing that for the first time- like a new found freedom.
But there are risks associated with this method. When I've worked with Node.js, I was using socket.io and tried to associate the userid with the socket by setting .id. Well, I accidentally overwrite the socket.io's existing .id field. No compiler or runtime warned me I was doing so.
So while it's great for general purpose applications, I think in a safety critical system a static type paradigm would be much more better. This may not necessarily be classed-based, but a language where a sub module can't arbitrarily overwrite some critical object you pass in, where parameter and return types and interfaces are formally specified.
Of course, you could argue that you could get this safety in a prototype-based language by hiding the actual critical object inside of a closure and returning an interface to it, so even if your submodule messes up the interface, the wrapped object is still safe, for example:
Code: Select all
// returns a safe controller interface so subcomponents can't mess up the missile controller object
createMissileControllerInterface = function(missileController) {
// test if it implements the interface correctly
if(!(missileController.fired is "function") ||
!(missileController.hasFired is "function") ||
!(missileController.setAngle is "function") ||
!(missileController.getAngle is "function") {
throw "Argument doesn't implement all of the functions of a missile controller.";
}
// if your language supports it, check how many parameters each of those functions take
// return an interface
return {
fire: function() { missileController.fire(); },
hasFired: function() { return missileController.hasFired(); },
setAngle: function(angle) { missileController.setAngle(angle); },
getAngle: function() { return missileController.getAngle(); }
};
};
// create msInterface around ms
msInterface = createMissileControllerInterface(ms);
// you can now pass msInterface to your unsafe subcomponents without worrying about them messing up the mission-critical ms object
Code: Select all
someFunction(const missileInterface);
Code: Select all
return {
rootObject: const missileController
};
My OS is Perception.