4 ms·
There is a reason C++ devs and only C++ devs have nightmares of diamond inheritance. Oh the damage that language has done to a generation, but at least it is l
by Scubabear68 11mo ago
There is a reason C++ devs and only C++ devs have nightmares of diamond inheritance.
Oh the damage that language has done to a generation, but at least it is largely passed us now.
- Spivak 11mo agoI'm spoiled by Python's incredibly sane inheritance and I always have to keep in mind that inheritance is a very different beast in other languages.
- kevindamm 11mo agoAnd python didn't get it right the first time either. It wasn't until python 2.3 when method resolution order was decided by C3 linearization that the inheritance in python became sane. http://mail.python.org/pipermail/python-dev/2002-October/029035.html http://mail.python.org/pipermail/python-dev/2002-October/029...
- mekoka 11mo agoInheritance being "sane" in Python is a red herring for which many smart people have fallen (e.g. https://www.youtube.com/watch?v=EiOglTERPEo https://www.youtube.com/watch?v=EiOglTERPEo). It's like saying that building a castle with sand is not a very good idea because first, it's going to be very difficult to extract pebbles (the technical difficulty) and also, it's generally been found to be a complicated and tedious material to work with and maintain. Then someone discovers a way to extract the pebbles. Now we have a whole bunch of castles sprouting that are really difficult to maintain.
- xdennis 11mo agoI'm always surprised by how arrogant and unaware Python developers are. JavaScript/C++/etc developers are quite honest about the flaws in their language. Python developers will stare a horrible flaw in their language and say "I see nothing... BTW JS sucks so hard.". Let me give you just one example of Python's stupid implementation of inheritance. In Python you can initialize a class with a constructor that's not even in the inheritance chain (sorry, inheritance tree because Python developers think multiple inheritance is a good idea). class A: def __init__(self): self.prop = 1 class B: def __init__(self): self.prop = 2 class C(A): def __init__(self): B.__init__(self) c = C() print(c.prop) # 2, no problem boss And before you say "but no one does that", no, I've see that myself. Imagine you have a class that inherits from SteelMan but calls StealMan in it's constructor and Python's like "looks good to me". I've seen horrors you people can't imagine. * I've seen superclass constructors called multiple times. * I've seen constructors called out of order. * I've seen intentional skipping of constructors (with comments saying "we have to do this because blah blah blah) * I've seen intentional skipping of your parent's constructor and instead calling your grandparent's constructor. * And worst of all, calling constructors which aren't even in your inheritance chain. And before you say "but that's just a dumb thing to do", that's the exact criticism of JS/C++. If you don't use any of the footguns of JS/C++, then they're flawless too. Python developers would say "Hurr durr, did you know that if you add a object and an array in JS you get a boolean?", completely ignoring that that's a dumb thing to do, but Python developers will call superclass constructors that don't even belong to them and think nothing of it. ------------------------------ Oh, bonus point. I've see people creating a second constructor by calling `object.__new__(C)` instead of `C()` to avoid calling `C.__init__`. I didn't even know it was possible to construct an object while skipping its constructor, but dumb people know this and they use it. Yes, instead of putting an if condition in the constructor Python developers in the wild, people who walk among us, who put their pants on one leg at a time like the rest of us, will call `object.__new__(C)` to construct a `C` object. def init_c(): c2 = object.__new__(C) c2.prop2 = 'three' print(c2.__dict__, type(c2)) # {'prop2': 'three'} <class '__main__.C'> And Python developers will look at this and say "Wow, Python is so flawless".
- drekipus 11mo ago1. Your first example is very much expected, so I don't know what's wrong here. 2. Your examples / post in general seems to be "people can break semantics and get to the internals just to do anything" which I agree is bad, but python works of the principle of "we're all consenting adults" and just because you can, doesn't mean you should. I definitely don't consent to your code, and I wouldn't allow it to be merged in main. If you or your team members have code like this, and it's regularly getting pushed into main, I think the issue is that you don't have safeguards for design or architecture The difference with JavaScript "hurr durr add object and array" - is that it is not an architectural thing. That is a runtime / language semantics thing. One would be right to complain about that
- zelphirkalt 11mo ago> The difference with JavaScript "hurr durr add object and array" - is that it is not an architectural thing. That is a runtime / language semantics thing. One would be right to complain about that Exactly. One is something in plain sight in front of ones eyes, and the other one can be well hidden, not easy to spot.
- kccqzy 11mo agoOh I've seen one team constructing an object while skipping the constructor for a class owned by another team. The second team responded by rewriting the class in C. It turns out you cannot call `object.__new__` if the class is written in native code. At least Python doesn't allow you to mess around when memory safety is at stake.
- maleldil 11mo agoFor what it's worth, pyright highlights the problem in your first example: t.py:11:20 - error: Argument of type "Self@C" cannot be assigned to parameter "self" of type "B" in function "__init__" "C*" is not assignable to "B" (reportArgumentType) 1 error, 0 warnings, 0 information ty and pyrefly give similar results. Unfortunately, mypy doesn't see a problem by default; you need to enable strict mode.
- Spivak 11mo ago
- anon291 11mo agoPython is slightly better because it can mostly be manipulated beyond recognition due to strong metaprogramming but pythons operator madness is dangerous. Random code can run at any minute. It's useful for something's and a good scripting language, and a very well designed one, no question there. Still it would be better if it supported proper type classes. It could retain the dynamic typing, just be more sensible.
- lll-o-lll 11mo agoDiamond inheritance is its own special kind of hell, but “protected virtual” members of java and c# are the “evil at scale” that’s still with us today. An easy pattern that leads to combinatorial explosion beyond the atoms in the universe. Trivially. People need to look at a playing deck. 52 cards, and you get 8×10^67 possible orders of the deck. Don’t replicate this in code.
- DeathArrow 11mo agoWhy do protected virtual methods lead to an explosion?
- bheadmaster 11mo agoProtected = subclasses can call them. Virtual = subclasses can override them. So basically, any subclass can call the method, and that method may be overridden in any other subclass.
- zelphirkalt 11mo agoWhat is the issue with those overrides? They only affect that one path in the hierarchy of inheritance, no? Not a C++ user here, but I imagine it would be catastrophic, if an unrelated (not on path to root superclass) class could override a method and affect unrelated classes/objects.
- bheadmaster 11mo ago> They only affect that one path in the hierarchy of inheritance, no? Not necessarily. If you create a diamond (or a spiderweb :) inheritence pattern, the amount of places the method can be called and overriden grows fast.
- mrsmrtss 11mo agoAt least C# methods are not virtual by default like in Java.
- kccqzy 11mo agoEvery language that permits diamond inheritance causes the devs who dare to use this feature at least some nightmare. It's not a C++ issue.
- nine_k 11mo agoIt's also cultural, possibily. Python supports diamond inheritance, and clearly states how it handles it (it ends up virtual in C++ terms). But in like 20 years of working with Python I can't remember encountering diamond inheritance in the wild once.
- kccqzy 11mo agoDiamond inheritance is in fact highly pervasive in Python. The reason is that every class is a subclass of object since Python 3 (Python 2 allows classic classes that are different). So every single time you use multiple inheritance you have diamond inheritance. Some of this diamond inheritance is totally innocuous, but mostly not, because a lot of classes override dunder methods on object like __setattr__. It was Guido van Rossum himself that observed the prevalence of diamond inheritance that led to Python 2.3 fixing the MRO, and introducing the super() function to make multiple inheritance sane. You should read his essay: https://www.python.org/download/releases/2.2/descrintro/ https://www.python.org/download/releases/2.2/descrintro/
- dragonwriter 11mo ago> Diamond inheritance is in fact highly pervasive in Python. I don't think that's true, because... > So every single time you use multiple inheritance you have diamond inheritance. Multiple inheritance is supported but not itself “highly pervasive” in Python > It was Guido van Rossum himself that observed the prevalence of diamond inheritance The essay you link does not support that claim. He doesn’t observe an existing prevalence, he describes new features being added simultaneously with the MRO fix that would present new use cases where diamond inheritance may be useful. And, its true, diamond inheritance is more common in modern Python than it was with classic classes in ancient Python, but there is a huge leap between that and “highly pervasive”.
- creata 11mo ago> at least it is largely passed us now What does this mean? There doesn't seem to be a popular alternative to C++ yet, unfortunately.
- klardotsh 11mo agoAside from game dev, Rust is being used in quite a lot of green field work where C++ would have otherwise been used. Game dev world still has tons of C++, but also plenty of C#, I guess. Agreed that it’s not really behind us though. Even if Rust gets used for 100% of C++’s typical domains going forward (and it’s a bit more complicated than that), there’s tens? hundreds? of millions (or maybe billions?) of lines of working C++ code out there in the wild that’ll need maintained for quite a long time - likely order decades.
- Animats 11mo agoThe problem in Rust is that if B is inside of A, struct A { name: String, owned: B } struct B { name: String, } you can't have a writeable reference to both A and B at the same time. This is alien to the way C/C++ programmers think. Yes, there are ways around it, but you spend a lot of time in Rust getting the ownership plumbing right to make this work.
- vlovich123 11mo ago> you can't have a writeable reference to both A and B at the same time > but you spend a lot of time in Rust getting the ownership plumbing right to I think you maybe meant to say something different because here's the most obvious thing: impl A { fn simultaneously_writeable(&mut self) -> (&mut str, &mut str) { (&mut self.name, &mut self.owned.name) } } Now it may take you a while to figure out if you've never done Rust before, but this is trivial. Did you perhaps mean simultaneous partial field borrows where you have two separate functions that return the name fields mutably and you want to use the references returned by those functions separately simultaneously? That's hopefully going to be solved at some point, but in practice I've only seen the problem rarely so you may be overstating the true difficulty of this problem in practice. Also, even in a more complicated example you could use RefCell to ensure that you really are grabbing the references safely at runtime while side-stepping the compile time borrow checking rules.
- jeffrallen 11mo agoCurious quirk of history that C++ peaked when Gen X was comiyof age, who were disproportionately affected by lead poisoning.
- spacechild1 11mo agoI haven't encountered diamond inheritance a single time in 10 years of writing/reading C++, so I definitely don't have nightmares about it. Maybe that was really a thing in the 90s or 2000s?
- gpderetta 11mo agoI have been programming professionally in c++ for 20 years. I remember once thinking "cool, I could use virtual inheritance here". I ended up not needing it. MI is not an issue in c++, and if it were the solution would be virtual inheritance.
- StellarScience 11mo agoExactly. Unlike Java where every object inherits from Ojbect, in C++ multiply inheriting from objects with a common base class is rare. Some older C++ frameworks give all their objects a common base class. If that inheritance isn't virtual, developers may not be able to multiply inherit objects from that framework. That's fine, one can still inherit from classes outside the framework to "mix in" or add capabilities. I've never understood the diamond pattern fear-mongering. It's just a rarely-encountered issue to keep in mind and handle appropriately.
- spacechild1 11mo ago> in C++ multiply inheriting from objects with a common base class is rare. One example is COM (or COM-like frameworks) where every interface inherits from IUnknown. However, there is no diamond problem because COM interfaces are pure abstract base classes and the pure virtual methods in IUnknown are implemented only once in the actual concrete class.