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
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/4TYINLQK7DJMI44F6ETLPNEIQUTVULD4/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to