4 ms·
It is a good idea because it's the most fundamental idea. You have two objects. A and B. How do you merge the two objects? A + B? The most straight forward wa
by ninetyninenine 1y ago
It is a good idea because it's the most fundamental idea.
You have two objects. A and B. How do you merge the two objects? A + B?
The most straight forward way is inheritance. The idea is fundamental.
The reason why it's not practical has more to do with human nature and the limitations of our capabilities in handling complexity then it has to do with the concept of inheritance itself.
Literally think about it. How else do you merge two structs if not using inheritance?
The idea that inheritance is not fundamental and is wrong in nature is in itself mistaken.
- josephg 1y ago> Literally think about it. How else do you merge two structs if not using inheritance? What? Using multiple inheritence? That's one of the worst ideas I've ever seen in all of computer science. You can't just glue two arbitrary classes together and expect their invariants to somehow hold true. Even if they do, what happens when both classes implement a method or field with the same name? Bugs. You get bugs. I've been programming for 30 years and I've still never seen an example of multiple inheritance that hasn't eventually become a source of regret. The way to merge two structs is via composition: struct C { a: A, b: B, } If you want to expose methods from A or B, either wrap the methods or make the a or b fields public / protected and let callers call c.a.foo(). Don't take my word for it, here's google's C++ style guide[1] > Composition is often more appropriate than inheritance. > Multiple inheritance is especially problematic, because it often imposes a higher performance overhead (in fact, the performance drop from single inheritance to multiple inheritance can often be greater than the performance drop from ordinary to virtual dispatch), and because it risks leading to "diamond" inheritance patterns, which are prone to ambiguity, confusion, and outright bugs. > Multiple inheritance is permitted, but multiple implementation inheritance is strongly discouraged. [1] https://google.github.io/styleguide/cppguide.html#Inheritance https://google.github.io/styleguide/cppguide.html#Inheritanc...
- ninetyninenine 1y ago>What? Using multiple inheritence? You just threw this in out of nowhere. I didn't mention anything about "multiple" inheritance. Just inheritance which by default people usually mean single inheritance. That being said multiple inheritance is equivalent to single inheritance of 3 objects. The only problem is because two objects are on the same level it's hard to know which property overrides which. With a single chain of inheritance the parent always overrides the child. But with two parents, we don't know which parent overrides which parent. That's it. But assume there are 3 objects with distinct properties. A -> B -> C would be equivalent to A -> C <- B. They are isomorphic. Merging distinct objects with distinct properties is commutative which makes inheritance of distinct objects commutative. C -> B -> A == A -> B -> C >I've been programming for 30 years and I've still never seen an example of multiple inheritance that hasn't eventually become a source of regret. Don't ever tell me that programming for 30 years is a reason for being correct. It's not. In fact you can be doing it for 30 years and be completely and utterly wrong. Then the 30 years of experience is more of a marker of your intelligence. The point is YOU are NOT understanding WHAT i am saying. Read what I wrote. The problem with inheritance has to do with human capability. We can't handle the complexity that arises from using it extensively. But fundamentally there's no OTHER SIMPLER way to merge two objects without resorting to complex nesting. Think about it. You have two classes A and B and both classes have 90% of their properties shared. What is the most fundamental way of minimizing code reuse? Inheritance. That's it. Say you have two structs. The structs contain redundant properties. HOW do you define one struct in terms of the other? There's no simpler way then inheritance. >> Composition is often more appropriate than inheritance. You can use composition but that's literally the same thing but wierder, where instead of identical properties overriding other properties you duplicate the properties via nesting. So inheritance A = {a, b}, C = {a1}, A -> C = {a1, b} Composition: A = {a, b}, C = {a1}, C(A) = {a1, {a, b}} That's it. It's just two arbitrary rules for merging data. If you have been programming for 30 years you tell me how to fit this requirement with the most minimal code: given this: A = {a, b, c, d} I want to create this: B = {a, b, c, d, e} But I don't want to rewrite a, b, c, d multiple times. What's the best way to define B while reusing code? Inheritance. Like I said the problem with inheritance is not the concept itself. It is human nature or our incapability of DEALING with the complexity that arises from it. The issue is the coupling is two tight so you make changes in one place it creates an unexpected change in another place. Our brains cannot handle the complexity. The idea itself is fundamental not stupid. It's the human brain that is too stupid to handle the emergent complexity. Also I don't give two flying shits about google style guides after the fiasco with golang error handling. They could've done a better job.
- matkoniecz 1y ago> Say you have two structs. The structs contain redundant properties. HOW do you define one struct in terms of the other? There's no simpler way then inheritance. why inheritance would make it easier?
- ninetyninenine 1y agoI have: A = {a, b, c, d, e} I want to write: B = {a, b, c, d, e, f, g} But I don't want to write duplicate code So I write: B = B(A) = {A, e, f, g} aka I use inheritance. Are there easier ways? No. Inheritance is the most fundamental way of doing this. Composition is just a work around as it results in arbitrary nesting. But ultimately it's the same thing too.
- josephg 1y ago> But assume there are 3 objects with distinct properties... Why would we assume that? If the objects are entirely distinct, why are you combining them together into one class at all? That doesn't make any sense. Let distinct types be distinct. Let consumers of those types combine them however they like. > But fundamentally there's no OTHER SIMPLER way to merge two objects without resorting to complex nesting. [...] You have two classes A and B and both classes have 90% of their properties shared. What is the most fundamental way of minimizing code reuse? Inheritance. That's it. So now the objects have 90% of their properties shared. That's different from what you were saying earlier. But moving on... The composition answer is similar to the inheritance answer. Take the common parts and extract them out into a self contained type. Use that type everywhere you need it - eg by including it in both A and B. > given this: A = {a, b, c, d} I want to create this: B = {a, b, c, d, e}. [...] What's the best way to define B while reusing code? Via composition, you do it like this: B = { a: A, e } What could be simpler than that? You keep claiming that inheritance is simpler. But then you go on to say this: > The problem with inheritance has to do with human capability. We can't handle the complexity that arises from using it extensively. [..] It is human nature or our incapability of DEALING with the complexity that arises from it. The issue is the coupling is two tight so you make changes in one place it creates an unexpected change in another place. Our brains cannot handle the complexity. In other words, using inheritance increases the complexity of the resulting code for the human brain. It makes our code harder to read & understand. I absolutely agree with this criticism you're making. And that's an incredibly damning criticism, because complexity is an absolute killer. Your capacity to wrangle the complexity of a given piece of code is the single greatest limitation of any software developer, no matter how skilled. Any change that makes your code more complex and harder to understand must be worth it in some way. It must pay dividends. Using inheritance brings no benefit in most cases over the equivalent compositional code. The only thing that happens is that - as you say - it makes the software harder for our brains to handle. If you ask me, that's a terrible decision to make.
- iExploder 1y agoI guess the point was never how to do thing properly, but: "how to join two struts with least amount of work and thinking so my manager can tick off a box in excel" in such case inheritance is a nice temporary crutch
- RHSeeger 1y ago> Even if they do, what happens when both classes implement a method or field with the same name? It's done in Java with interfaces with default implementations, and the world hasn't imploded. It just doesn't seem like that big of a problem.
- josephg 1y ago> It just doesn't seem like that big of a problem. It really, really depends on the codebase. There are absolute mammoth tire fire codebases out there - particularly in "enterprise code". These are often made up of insane hierarchies of classes which in practice do nothing but obscure where any of the actual logic lives for your program. AbstractFactoryBuilderImpl. Wild goose chases where you need some bizzaire and fragile incantation to actually create an object, because every class somehow references every other class. And you can't instantiate anything without instantiating everything else first. If you haven't seen it in your career yet (or at all), you are lucky. But I promise you, hell is programmed by mediocre teams working in java.
- RHSeeger 1y agoI've seen such difficult to follow code in Java, and I complain about it every time. But having multiple inheritance isn't the cause (because Java doesn't have that). Nor does having multiple interfaces with default implementations have any real impact (at least in _any_ case I've seen). The only real difference I see between multiple inheritance and multiple interfaces with default implementations is constructors. And they can be handled in the same way as default implementations; requiring specific usage/ordering.
- astrobe_ 1y agoI found myself in a situation where I had to reinvent data structures from scratch in an assembly-like language: "properties" or "fields" are just offsets relative to a pointer (runtime) or an address (compile-time). The need to extend a data structure to add more fields comes almost immediately. Think: something like the C "hack" of embedding the "base" structure as the first field of the "derived" structure: struct t_derived { struct t_base base; int extra_data; }; Then you can pass a derived_t instead of base_t with some type casting and caveats. This is "legal" in C because the standard guarantees that base has offset 0. Of course our "extra_data" could be a structure, but although it would look like "A+B" it is actually a concatenation.
- dgb23 1y ago> How else do you merge two structs if not using inheritance? By merging them. Structs are product types. If you merge them, you get a bigger product type. You don't need inheritance (ADTs) for that. The more useful point of inheritance is having shared commonality. But modern languages make it convenient to express that without using ADTs/inheritance. TypeScript is fully structurally typed. If you combine a Foo and a Bar it is something new, but keeps being both a Foo and a Bar as well. Go is structurally typed to a relatively high degree as well. You can embed types (including structs) into structs and only care about the individual parts in your functions. And you have composable and implicit interfaces. Clojure has protocols and generally only cares about the things you use or define to use in functions. It allows you to do hierarchical keyword ontologies if you want, but I see it rarely used. These languages and many others favor two fundamental building blocks: composition and signatures. The latter being either about data fields or function signatures. The neat part is these aren't entangled: You can use and talk about them separately. How fundamental is inheritance if it can be fully replaced by simpler building blocks?
- ninetyninenine 1y ago>By merging them. Structs are product types. If you merge them, you get a bigger product type. You don't need inheritance (ADTs) for that. Merging structs and inheritance are fundamentally the same thing. >How fundamental is inheritance if it can be fully replaced by simpler building blocks? It can't be replaced. Combining Foo and Bar in the way you're thinking involves additional primitives and concepts like nesting. If Foo and Bar share a same property the most straight forward way of handling is overriding one property with the other. Overriding IS inheritance. We aren't dealing with product types in the purest form either. These product types have named properties and you need additional rules to handle conflicting names. In fact once you have named properties the resulting algebra from multiplying structs is not consistent with the concept of multiplication whether you use inheritance or "object composition"