
On Sun, Apr 17, 2022 at 07:39:29PM +1200, Greg Ewing wrote:
On 16/04/22 10:26 pm, Steven D'Aprano wrote:
C++ and Eiffel are even stricter (more restrictive) than Python. They don't just exclude class hierarchies which are inconsistent, they exclude class hierarchies with perfectly good linearizations because they have a method conflict.
No, they don't *exclude* such hierarchies, they just require you to resolve the conflicts explicitly.
Okay, fair comment. Can we agree on this then? - C++ allows hierarchies with method conflicts so long as you do not implicitly inherit from those methods. (You must explicitly call the superclasses you want.) - Eiffel allows hierarchies with method conflicts so long as you remove the conflict by renaming the methods.
no matter how many times I say that other choices for MI are legitimate and maybe even better than Python's choice
So by saying that something is "not full MI", you didn't mean to imply that it is somehow inferior and less desirable?
Well done! I'm glad we're making progress. https://www.youtube.com/watch?v=Cl2pvI1nQVU How many times did I suggest that the C++ or Eiffel approach might be better than Python's approach? How many times did I mention traits, or link to Michele Simionato's blog? I linked to James Knight's (misnamed) post "super considered harmful" at least twice. It is certainly worth reading to understand some of the problems with Python's MI. If I say that Python's model of MI is more general, I mean *more general*. Its not a dog-whistle for Python-supremacists. It just means that Python handles more cases than Eiffel or C++ it doesn't imply that those languages are "inferior". For what its worth, I think that fully general MI does fit into what Paul Graham calls the "Blub Paradox". It's *more powerful* -- but it might be *too powerful* to use effectively, like unrestrained GOTO capable of jumping into the middle of functions, or Ruby's ability to monkeypatch everything including builtins. If all I want to do is drive to the local corner store and buy milk, a rocket-car that does Mach 3 in a straight line and burns 2000 gallons of fuel a minute is more powerful, but not as useful as a 30 year old Toyota. Sometimes less is more. If you're having problems with MI maybe what you need is *less of it* not more of it, and perhaps that means getting the compiler to warn you when you've attached a JATO rocket to your Toyota sedan, which is what C++ and Eiffel and Squeak traits do in different ways.
Because that's what you sounded like you were saying, and why everyone is pushing back so hard on it.
Yeah Greg, you got me, you saw through my cunning plan to denigrate C++ and Eiffel by saying that they are better than Python.
The requirement for automatic conflict resolution is kinda necessary for it to be inheritance
You seem to have a very different idea of what "inheritance" means from everyone else here.
Well, I don't know about "everybody else", but okay. I think we agree that when we do this: instance.method() the interpreter automatically searches for some class in a tree of superclasses where the method is defined, and we call that inheritance. I think that the distinguishing feature here is that the interpreter does it *automatically*. If we had to manually inspect the tree of superclasses and explicitly walk it, it wouldn't be inheritance: # Pretend that classes have a parent() method and we only # have single inheritance. instance.__class__.parent().parent().parent().method(instance) which is just another way of explicitly calling a method on a class: GreatGrandParentClass.method(instance) So we're not inheriting anything there, we're just calling a function: some_namespace.function(instance) And if you're just calling a function, then it really doesn't matter whether GreatGrandParentClass is in the instance's MRO or not. If the MRO isn't used, then why even have an MRO? What else could distinguish inheritance from just "call some function", if it isn't that the interpreter works out where the function is defined for you, using some automatic MRO? (That's not a rhetorical question. If you have some other distinguishing characteristic in mind, apart from automatically searching the MRO, then please do speak up, I would love to hear it.) Take that automatic MRO search away, and you're not doing inheritance, you're just calling a function in a namespace. **Which is fine.** That's the basis of composition and delegation. That's not worse than MI. Its better. (But note that this doesn't distinguish between inheritance and generics, which also has a form of automatic delegation. Oh well.) -- Steve