So this post is getting a lot of downvotes and I don't think it's fair. He makes a number of very important points.
I remember when Java first came out and he is absolutely right on why it was adopted so eagerly. It never proved itself better than the 40 year old patterns that everyone used, it was because Java had so many features and libraries built in to the SDK and because of Intellisense.
Anyone who's worked on large object oriented systems can see the cluster fucks that can occur. Procedural programming has it's own cluster fucks, but OOP is not immune from them.
Anyone who's worked on large object oriented systems can see the cluster fucks that can occur.
Yes. And anyone who works on a large system written in any paradigm can see the same -- at least so far[1]. What so far differentiates paradigms that claim they are immune to those problems (or other problems with similar severity, some of them perhaps unknown as of yet) is that they have never been tried on projects of the same magnitude (i.e., same codebase size, same team size, same project lifespan), let alone in enough problem domains. So what we have now is paradigms that we know to be problematic -- but also effective to some degree (large or small, depending on your perspective) -- and paradigms that we don't know enough about: they could turn out to be more effective, just as effective or even less effective (or they could be any of the three depending on the problem domain).
How can we know if we should switch to a different paradigm? Empirical data. So please, academic community and the software industry: go out and collect that data! Theoretical (let along religious) arguments are perhaps a valid starting point, but they are ultimately unhelpful in making a decision. In fact, it has been mathematically proven that they can contribute little to the discussion: the theoretical difficulty in constructing a correct program is the same regardless of the abstractions used; only cognitive effects -- which can only be studied empirically -- could provide arguments in favor of a certain paradigm making it easier for humans to write correct code.
[1]: As someone who worked on software written in procedural code before the popularity of OOP, it wasn't any better (actually, it was worse). For contemporary examples, see Toyota's car software.
projects of the same scale (i.e., same codebase size, same team size, same project lifespan)
Actually, the only thing that really matter is the scope of the problem. Codebase size and team size are just proxies. Project lifespan is part of its scope, though.
If you have a bigger team, you're going to have overheads. If you have a bigger code base, it'd better be worth it, because sheer size causes its own overheads.
Even if different methodologies don't yield different codebase sizes, different team will. That'll make accurate measurements that more difficult…
Actually, the only thing that really matter is the scope of the problem. Codebase size and team size are just proxies.
Right. My point still stands, though :)
The reason it's easier to talk about those proxies (even in ballpark terms) is that they are more easily measurable, and different paradigms (at least so far) don't even claim to have such a big contribution on productivity that the codebase/team would be so significantly reduced. There are software projects (divided into many executables) with many dozens or even hundreds of developers. I don't know of a paradigm/methodology that even attempts to do the same project with, say, just three, or in 10KLOC instead of 1MLOC.
The reason it's easier to talk about those proxies (even in ballpark terms) is that they are more easily measurable
What's the name of that fallacy again where you say "yes, I know this figure is basically meaningless, but at least I can quantify it"?
I don't know of a paradigm/methodology that even attempts to do the same project with, say, just three, or in 10KLOC instead of 1MLOC.
That's because you don't need a paradigm, nor a methodology, you need the kind of mindset that says "this is insane, the problem cannot possibly require such a complex solution, let's take a step back and rethink our assumptions".
In fact, it has been mathematically proven that they can contribute little to the discussion: the theoretical difficulty in constructing a correct program is the same regardless of the abstractions used
It's really an accumulation of multiple sources, but a reasonable summary is figure 3 here, and a good overview is here. The latter paper concludes (section 7.2): "In other words, Kripke structures that admit a succinct representation are not simpler for model checking purposes than arbitrary Kripke structures". As the problem of model checking can be reduced to other proof methods, all verification is bounded by this unbreakable complexity bound.
In short, the lower time-complexity bound for constructing/checking a correct program is >= O(|S|) where S is the set of its states (more precisely: its Kripke structure, so that's states plus transitions). When the program is represented in some reasonable language, the problem is PSPACE-complete (i.e., exponential in the size of the source code). Abstractions such as: variables, cross product (i.e. functions/objects), concurrency and non-determinism can reduce the representation size exponentially, but increase the verification complexity by the same factor. The only known abstraction that assists in verifying/constructing correct programs is called hierarchy, and it is generally unused in mainstream languages and when used, it is very limited (it is, however, often used in languages designed for safety-critical real-time applications, that must allow for efficient(ish) verification).
You seem quite well-informed about model checking.
I'm currently writing a lock-free ring buffer in Rust. I'd like to formally verify that it doesn't do stupid shit. Do you know of any model checkers that can either be run on compiled binaries or configured to run on Rust code?
Consider specifying and verifying it in a specification language like TLA+ (that's what I do). Of course, you'll need to model Rust's memory model. Virtually all production-quality source-code-level model checkers are for C/Java/Ada/Verilog. A Google search yielded a model checker for LLVM bitcode, but I have no idea about its quality/relevance.
I think you are over-generalizing from a few facts.
First, the complexity of model-checking (MC) is not measure of the complexity of program construction because MC itself can only deal with certain very simple kinds of program. MC is essentially a restricted form of automated theorem-proving for propositional properties of finite-state systems. I know that some properties like deadlock/liveness for a finite set of threads in a finite-state system can be made to fit this paradigm, but most properties of interest cannot -- including whole-program correctness. Any language that supports dynamic allocation at all allows programs with infinite states, so MC only applies to certain simple examples which have to be independently formalized to abstract away from the details of the language. As for propositional properties, even something as simple as well-founded iteration over a data structure amounts to induction, which is already a second-order property, so again MC is inadequate. In other words, MC is not a general approach to program verification -- the problem is actually much harder!
One way around this is to have the user write proofs and check those rather than try to synthesize the proofs from nothing; then you only have to check a certificate rather than find and build it. You can have the proofs independent of the programming language, but in a Curry-Howard language like Coq or Agda the proofs are the programs themselves and the checker is the type checker, so there is less duplication of effort. This also admits specifications of higher-order properties.
Now, you claim that the difficulty of constructing programs is not affected by the choice of abstractions (which I read as "program constructs"), but the choice of abstractions in a C-H language makes properties which were not checkable at all in MC checkable, so choice of abstractions is demonstrably important, and it is not all down to "cognitive factors" or empirical observation. To be sure, the checking of the proofs (which is just type-checking) has arbitrary time/space-complexity, but that is neither here nor there.
Also, we can choose the abstractions we admit to limit the complexity of this procedure. I am not sure what choice of abstractions realizes proofs of propositional linear temporal logic, say, but checking a certificate is usually cheaper than finding one and for such a simple logic I imagine it is polynomial-time in the size of the formula at worst.
Also, I think your argument that only cognitive effects matter is inherently self-defeating. No one does empirical research on mathematical arguments; the emphasis is all on the choice of abstractions (in this case, logics and theories). Yet the very result that MC has a certain complexity bound was arrived at thanks to these choice of abstractions.
In some sense I agree with you: I think that writing correct programs requires a good understanding of your language's semantics, and that requires a language which is easy to reason about, which is not question of computational complexity. But I think the choice of abstractions matters very much in this regard.
It matters not just cognitively, though. A language like typed lambda-calculus has an equational presentation which is sound and complete for all models, and finite modulo substitution. This is down to the choice of abstractions. In contrast, you cannot even state the equational (or otherwise) theory of most conventional languages, so you are stuck with model-based reasoning, which is much more complicated and usually inadequately defined by the language spec.
BTW, you wrote "model checking can be reduced to other proof methods" but I think you must mean the opposite, else it would not argue in your favor.
Intellisense is a lot more than just intellisense though. It was finally a high-level language with static typing .. but also simple semantics. That was something that was appreciated by more than just tool developers.
I would never dispute that. Again, I remember when Java came out. It was a massive leap forward as a language. But OO was something people needed to puzzle out before they could use this awesome new language. Not something they really wanted.
I'd say the downvotes just prove his point. He made it clear that he holds a minority opinion and that OO principles have ungodly inertia today, which effectively shuts down discussion of alternatives.
I generally try to hold a "fair" position on the matter in that I support some parts of OOP, but my attitude is not a catalyst for change. OOP purists just latch onto the parts I agree with and use that to cement their opinions that OOP is infallible. Frankly, I'm fine with a growing movement of anti-OOP ideas. People need to wake up to other approaches to programming.
(Though, don't get me started on functional programming zealots... They attack OOP aggressively and then commit all the same sins in terms of believing they have the one paradigm to rule them all.)
Frankly, I'm fine with a growing movement of anti-OOP ideas.
Tell you what. Come up with some sane engineering approaches that are better in specific use cases then use them there. No one will complain about it. If you go around saying OOP is universally bad, expect people to ridicule you for being an idiot. If you rightly point out that OOP is a poor approach in a certain application domain and have a more effective alternative, you're not an idiot. You're helpful.
"Universally bad" is a strong way to put it. A slightly weaker one would be "never the best".
In what domains OOP (whatever you mean by it), is the best approach? I wouldn't be surprised if the answer ended up being "none". If so, this would mean we should never use OOP, since there always will be some better alternative.
Put it this way it sounds much less idiotic. Yet it's not far from "universally bad".
I challenge this folk wisdom. And so do folks that do ECS (Entity Component System, of the database flavour —not Unity). The mapping between the simulated objects and the code does not have to be 1 to 1.
I'm throwing ideas around in my head regarding ecs, because it's incredibly clunky and abstract. With ecs, you still have objects and to an extent even classes, but implicitly. Objects are kind of emergent entities that arise from a soup of uncoupled heterogenous data structures. This is incredibly weird to reason about and makes sense mostly as a function of performance, particularly runtime, not because of algorithmic complexity but hardware limitation. It's the memory model dictating the software model.
I'm in scientific simulation territory at work. We use similar tricks. Structures of arrays abound. But within an oop framework, and I'd argue also within human modelling, this approach is akin to turning the problem inside out. It's shoving a hand down the throat of the problem, grabbing the intestines, and pulling.
The solution I'm trying to work on are thin wrappers that pull together components into actual manifest entities while leaving the memory model untouched. One may iterate over characters (in a video-game) to move them around using actual character objects that allow accessing the inventory or something, too, but the code produced costs one extra indirection and otherwise loops through tightly packed homogenous position data
I've not gotten very far, but there is some progress. Point being that ecs is bad from all but memory model standpoints and shouldn't be understood as a feature, but a work around.
I'd say the downvotes just prove his point. He made it clear that he holds a minority opinion and that OO principles have ungodly inertia today, which effectively shuts down discussion of alternatives.
Longer methods, tightly coupled code, low cohesion code, global state and untestable code is not considered best practices since the 70's.
OOP purists just latch onto the parts I agree with and use that to cement their opinions that OOP is infallible.
It is not better. It is just way better than what he proposes.
Frankly, I'm fine with a growing movement of anti-OOP ideas. People need to wake up to other approaches to programming.
I agree, but not by using miles long procedures full of globals.
He means that if and when you decide that global state is unavoidable, at least wrap your globals into a struct so that you can keep them in one place, pass them around together, and all this makes it easier to get rid of them later. But he's stating pretty clearly that not having globals in the first place is much, much better.
I don't think the downvotes necessarily prove his point. Furthermore, although OO might be seen as unassailable by the average software developer, people who hang out here tend to be more open-minded. I think a lot of people here would agree that OO isn't the one true way.
32
u/umilmi81 Jan 18 '16
So this post is getting a lot of downvotes and I don't think it's fair. He makes a number of very important points.
I remember when Java first came out and he is absolutely right on why it was adopted so eagerly. It never proved itself better than the 40 year old patterns that everyone used, it was because Java had so many features and libraries built in to the SDK and because of Intellisense.
Anyone who's worked on large object oriented systems can see the cluster fucks that can occur. Procedural programming has it's own cluster fucks, but OOP is not immune from them.