3 ms·
I still "believe in" OOP, but there's something that is increasingly troubling me lately: I keep thinking about the difference between the unbounded set of pos
by phaedrus 8y ago
I still "believe in" OOP, but there's something that is increasingly troubling me lately: I keep thinking about the difference between the unbounded set of possible derived classes which could be substituted for a base class, versus the small (probably 1-3) number of different actual implementations are ever used in a real program. It seems like this disparity indicates the mechanism we're using is overly general. Maybe this is an argument for Sum Types as opposed to runtime polymorphism.
It seems the biggest thing OOP polymorphism has going for it is that it is "open", that is, the possible derived classes need not be known in advance. But, the fact that our toolchains use a separate link step, and that linkers remain primitive and link-time code generation is not widespread, is merely an historical accident. There's no a priori reason things have to be this way.
For example, an alternative would be if all of the source for an executable were in a single image file a la Smalltalk, and compilation consisted of converting this "database of code" into a single executable (no shared libraries!) in one big-bang compilation step (no object files, no static libraries!) You could even have something which syntactically resembled open polymorphism, maybe even using existing languages almost as-is, but instead of compiling down to type erasure and dynamic pointer casts, would compile down to Sum Types (variants). (The compiler could scan the image to find all of the derived types it could be and could construct a variant out of that.)
I'm not saying this is better, just putting it forth as an example of how things could be radically different than they are now (even for statically typed, compiled languages), had we made different choices. Consider the alternative, the situation we have now: we use a technique which could support anywhere from 1 to infinity polymorphic derived classes to implement interfaces which probably have approximately 2 different implementations in a typical finished program.
Perhaps an analogy would help: If we approached hardware the way we approach software, we would be using connectors which support between 1 and infinity pins everywhere when connectors which support N pins would suffice.
- meheleventyone 8y agoI didn’t mean to do down OOP. I think there are a lot of useful concepts to be had looking at it. Polymorphism for extension is definitely one of those. It’s more a lot of the more ‘interesting’ takes I’ve seen to replace ifs come from that direction and seem to be an exercise in decreasing complexity in the small (easier to understand methods) whilst increasing it a lot in the large (harder to understand architecture). Your idea of replacing polymorphic variants with sum types sounds interesting. It also strikes me as quite similar to compile time polymorphism (e.g. generics) where you are 1 to infinity in the design space but the generated program boils down to only the variants created. Language wise I really like the mix of sum types, traits and generics in Rust.
- veebat 8y agoSum types are a huge benefit to branchy code. Philosophically, OOP polymorphism can do a similar thing, but it doesn't really cover it as succinctly: if your operation is of the "similar data, mostly the same algorithm but with variations at points depending on the data's type", going down the polymorphic route requires a lot more naming of things to cover each variation, and then when you go to inspect it, the code is twisty and jumps all over the place. I've had a lot of bugs introduced by method boundaries obscuring the execution flow. What polymorphism is good at is building the black box, the soft boundaries, and that isn't a good thing to do for fine-grained details. I do occasionally find a use for extension, but it's at a scale closer to "call this entire subprogram as a kind of state machine", vs "write this algorithm as a collaboration of objects abstracted away from each other". The "alternate hard and soft layers"[0] pattern comes to mind: if what you really need is modularity, going towards full dynamism and reflection seems like a better choice than to try to extend everything statically, which creates a very large latency issue(the default response to every form of change in a static system is "recompile from the beginning, precompute all answers"). At the same time, it's not a good fit to have a vast codebase do everything dynamically since the comprehensibility and throughput suffer. [0] http://wiki.c2.com/?AlternateHardAndSoftLayers http://wiki.c2.com/?AlternateHardAndSoftLayers