[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread David Mertz, Ph.D.
R has changed a bit since 2005. My article was from then:
https://gnosis.cx/publish/programming/R3.html

I'm not trying to quibble about R, but simple to point out that what often
mystified as "deep" about OOP is actually banal and fairly trivial.

That might be how Alan Kay originally saw OOP. He famously regretted using
> the term "object" because it distracted from what he saw as the genuinely
> fundamental parts of OOP, namely
>
> * Message passing
> * Encapsulation
> * Late (dynamic) binding


I like Alan. I worked with him a little bit. I agree with his attitude here.
___
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/SXHA3GUDZ7P5EO6RF3YB5LGGCOHE5XXV/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread Steven D'Aprano
On Sat, Apr 16, 2022 at 12:23:10PM -0400, David Mertz, Ph.D. wrote:

> R doesn't have inheritance, it's not OOP,

R is OOP and always has been. All values, arrays, functions etc in R are 
objects. Even expressions are objects. And it has inheritance.

https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Objects

https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Inheritance

R has three mechanisms for implementing classes, S3, S4 and Reference 
classes (unofficially known as S5). All three of them allow inheritance.

http://adv-r.had.co.nz/S3.html


> One thing I do find a bête noire is the silly claim, that Chris repeats,
> that inheritance expresses "Is-A" relationships.

"Is-a" is fundamental to the relationship between a class and its 
instances. Inheritance is orthogonal to that relationship, e.g. Swift 
only allows single inheritance. Every class can only have a single 
superclass, which defines what kind of thing the subclass is. But it can 
inherit from multiple mixins or traits, which allow it to inherit 
behaviour.

In Python, virtual subclassing defines that "is-a" relationship 
without inheriting anything from the parent class.


> The reality is more clear in the actual primary definition of the word
> itself: "the practice of receiving private property, titles, debts,
> entitlements, privileges, rights, and obligations".

That definition is incomplete when it comes to inheritance.

If I go to the store and purchase a bottle of milk, I have received 
private property. That's not inheritance.

If I receive a knighthood for slaughtering my monarch's enemies, that 
is also not inheritance.

My obligation to pay taxes when begin to earn income is another thing 
which I receive but is not inheritance.


> In programming, a class
> can receive methods and attributes from some other classes. That's all.
> It's just a convenience of code organization, nothing ontological.

That might be how Alan Kay originally saw OOP. He famously regretted 
using the term "object" because it distracted from what he saw as the 
genuinely fundamental parts of OOP, namely 

* Message passing
* Encapsulation
* Late (dynamic) binding 

Purists may also wish to distinguish between subclassing and subtyping. 
Raymond Hettinger has given talks about opening your mind to different 
models for subclassing, e.g. what he calls the "conceptual view" vs 
"operational view" of subclassing. Or perhaps what we might call "parent 
driven" versus "child driven".

https://www.youtube.com/watch?v=miGolgp9xq8

But whether we like it or not, the concepts of subclassing and subtyping 
are entwined. We model "is-a" concepts using classes; we implement code 
reuse using classes; we model taxonomic hierarchies using classes. 
Classes are flexible; they contain multitudes.



-- 
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/G6KLP7QVS3RXWJYNIEPX22DHJFUA6HZO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Auto assignment of attributes

2022-04-16 Thread Pablo Alcain
The problem of assigning init arguments as attributes has appeared several
times in the past (
https://mail.python.org/archives/list/python-ideas@python.org/message/VLI3DOFA5VWMGJMJGRDC7JZTRKEPPZNU/
was the most recent we could find) and is already handled in dataclasses.

Lately, discussing this topic with a friend, we thought that using a
specific token could be a possible approach, so you could do:

class MyClass:

def __init__(self, @a, @b, c):

pass

and it would be analogous to doing:

class MyClass:

def __init__(self, a, b, c):

self.a = a

self.b = b

Then, you would instantiate the class as usual, and the variables tagged
with `@` would be bound to the object:

>>> objekt = MyClass(2, 3, 4)

>>> print(objekt.b)

3

>>> print(objekt.c)

AttributeError: 'MyClass' object has no attribute 'c'


We have a working implementation here if anyone wants to take a look at:
https://github.com/pabloalcain/cpython/tree/feature/auto_attribute. Keep in
mind that we have limited knowledge about how to modify cpython itself, and
which would the best places be to do the modifications, so it's more than
likely that some design decisions aren't very sound (
https://devguide.python.org/grammar/ and https://devguide.python.org/parser/
were incredibly helpful).

Besides the implementation, we would like to know what the community thinks
on whether this might have any value. While developing this, we realized
that Crystal already has this feature (eg
https://github.com/askn/crystal-by-example/blob/master/struct/struct.cr)
with the same syntax; which is kind of expected, considering it's syntax is
based on Ruby.


Random collection of thoughts:

1. If auto-assignment made sense in general, one of the reasons we went for
this rather than the decorator approach is that we wouldn't like to have a
list of strings that can vary decoupled from the actual argument name.

2. The current implementation of `@` works for any function, not only init.
We don't know if this would actually be a desirable feature.

3. It also works with any function in the wild. This mostly allows for
monkey-patching to work out of the box:

>>> class Klass:

... def __init__(self):

... pass

...

>>> def add_parameter(k, @p):

... pass

...

>>> Klass.add_parameter = add_parameter

>>> objekt = Klass()

>>> print(objekt.p)

Traceback (most recent call last):

  File "", line 1, in 

AttributeError: 'Klass' object has no attribute 'p'

>>> objekt.add_parameter(11)

>>> print(objekt.p)

11

Again, we are not sure if this is desirable, but it's what made most sense
for us at the moment.

4. Adding the `@` token to the argument doesn’t remove the variable from
the function/method scope, so this would be perfectly valid:

>>> def my_function(k, @parameter):

... print(parameter)

>>> my_function(objekt, 4)

4

>>> k.parameter

4



5. We didn’t implement it for lambda functions.

Cheers,

Pablo and Quimey
___
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/SCXHEWCHBJN3A7DPGGPPFLSTMBLLAOTX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread David Mertz, Ph.D.
On Sat, Apr 16, 2022, 12:26 PM Chris Angelico > wrote:

> A button IS a widget.
> A horizontal box IS a box.
> A box IS a container.
> A container IS a widget.
>
> Class hierarchies in graphical systems are just as much based on those
> is-a relationships as any of those "horrible metaphors" you may have come
> across, and they are absolutely to do with programming. Just because you
> had a bad teacher, that doesn't mean the concepts are wrong.
>

Well, more like a few dozen bad books. I never studied any of this with a
teacher. But when I teach it (to quite a lot of programming professionals),
I never once mention that IS-A stuff, other than maybe curses sotto voce.
And mostly they come away saying they finally understood it after my
presentation.

>
___
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/5A3J6LRBADRYWC65MXHIQNHPQICLDDIA/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread Chris Angelico
On Sun, 17 Apr 2022 at 02:23, David Mertz, Ph.D.  wrote:
> One thing I do find a bête noire is the silly claim, that Chris repeats, that 
> inheritance expresses "Is-A" relationships. It was torture in the 1990s 
> hearing about vehicles and trucks and sedans as if that had something to do 
> with programming. I never "got" OOP for at least 5 years later than I should 
> have because of those horrible Linnaean metaphors.
>

A button IS a widget.

A horizontal box IS a box.

A box IS a container.

A container IS a widget.

Class hierarchies in graphical systems are just as much based on those
is-a relationships as any of those "horrible metaphors" you may have
come across, and they are absolutely to do with programming. Just
because you had a bad teacher, that doesn't mean the concepts are
wrong.

ChrisA
___
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/AXY5ZJORW2CWBCCH5H7MQ6HULUG4DPRT/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread David Mertz, Ph.D.
I don't really care what terms like "Full MI" or "True MI" are intended to
mean. Python and MANY other programming languages that allow classes to
have multiple parents, use a method resolution order (MRO) to decide which
same-named method from a complex inheritance tree to use in a given call.

If languages use an MRO, C3 is probably the best choice. But not the only
one. But sure, C++ and Eiffel are free to refuse "ambiguity" of names
occurring multiple times in an inheritance tree. Other languages are free
to use single inheritance, or traits, or mixins.

I wrote an article a lot of years ago, around the time C3 was new to
Python, intending to show what a banal thing OOP is in which I added
inheritance and an MRO to R (all within R itself). It was something like
20-30 lines of code in total. My MRO was something dumb like depth first or
breadth first, but it WAS an MRO. R doesn't have inheritance, it's not OOP,
which was my main reason to choose it for the exercise.

One thing I do find a bête noire is the silly claim, that Chris repeats,
that inheritance expresses "Is-A" relationships. It was torture in the
1990s hearing about vehicles and trucks and sedans as if that had something
to do with programming. I never "got" OOP for at least 5 years later than I
should have because of those horrible Linnaean metaphors.

The reality is more clear in the actual primary definition of the word
itself: "the practice of receiving private property, titles, debts,
entitlements, privileges, rights, and obligations". In programming, a class
can receive methods and attributes from some other classes. That's all.
It's just a convenience of code organization, nothing ontological.
___
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/CSSBRRM42NK7JMLMOEBL7JIY5ITPQC2W/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread Chris Angelico
On Sat, 16 Apr 2022 at 20:28, Steven D'Aprano  wrote:
> So please stop falsely accusing me of "No True Scotsman" bullshit. If
> you keep doing it, I will know you're not arguing in good faith.

Fine. I'll accuse you, instead, of using "full MI" to mean "the best
MI system that Steven D'Aprano can conceive". Happy now?

Because that is precisely what you are doing. Any argument to the
contrary is met with complaints that we need to show that something is
*better* for *Python*. For something to be "full MI", in your view, it
has to match what Python does, or be objectively better than it, in
your personal opinion.

I'm not going to bother responding to the rest of the details, as
they're all the same argument repeated.

ChrisA
___
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/3UATWEE4ICWWT42QHCW7DE2SLZZ2NDGZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: mro and super don't feel so pythonic

2022-04-16 Thread Steven D'Aprano
On Sat, Apr 16, 2022 at 05:27:57PM +1200, Greg Ewing wrote:
> On 15/04/22 10:37 pm, Steven D'Aprano wrote:
> >If you look at languages that implement MI, and pick the implementations 
> >which allow it with the fewest restrictions, then that is "full MI".
> 
> >I believe that Python (and other languages) allow MI with
> >the smallest set of restrictions, namely that there is a C3
> >linearization possible
> 
> But before Python adopted the C3 algorithm, it was less
> restrictive about the inheritance graph.

Less restrictive, and *inconsistent* (hence buggy).


> So by your definition, current Python does not do full MI!

No, I'm excluding inconsistent models of MI.

Correctness is a hard requirement. Otherwise we get into the ludicrous 
territory of insisting that every feature can be implemented with one 
function:

def omnipotent_function(*args, **kwargs):
"""Function which does EVERYTHING.

(Possibly not correctly, but who cares?)
"""
return None

There you go, now we can define an MRO for any class hierarchy 
imaginable, and dispatch to the next class in that hierarchy, using the 
same function. It won't work, of course, but if correctness isn't a hard 
requirement, what does that matter? :-)


> >If you have to manually call a specific method, as shown here:
> >
> >https://devblogs.microsoft.com/oldnewthing/20210813-05/?p=105554
> >
> >you're no longer using inheritance, you're doing delegation.
> 
> You could also say that Python automatically delegates to the first
> method found by searching the MRO.
> 
> Why is one of these delegation and not the other?

That is a very interesting question.

As I mentioned earlier, there is a sense that all inheritance is a kind 
of delegation, and in languages without an explicit super() (or 
"nextclass", or whatever you want to call it), the only way to get 
inheritance when you overload a method is to use explicit delegation to 
your superclass.

So we might say that all inheritance is delegation, but not all 
delegation is inheritance. We might even go further and say that any 
delegation to a superclass (not just the direct parent) is a form of 
manual inheritance.

But in general, in ordinary language, when we talk about inheritance, 
we're talking about two (maybe three) cases:

1. Automatic inheritance, when your class doesn't define a method but 
   automatically inherits it from its superclass(es).

class Parent:
def method(self): pass

class Child(Parent):
pass

assert hasattr(Child, "method")


2. Automatic inheritance when your class overloads a method and calls
   super() manually to delegate to the superclass(es).

class Child(Parent):
def method(self):
print("Overload")
super().method()

3. And more dubiously, but commonly used, when people who don't like 
   super(), or know about it, explicitly delegate to their  
   parent in single inheritance:

class Child(Parent):
def method(self):
print("Overload")
Parent.method(self)  # Oooh, flashbacks to Python 1.5



-- 
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/GOMMXQW4L6OJXO2425DZQE5AERGEMC5G/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: mro and super don't feel so pythonic

2022-04-16 Thread Steven D'Aprano
On Sat, Apr 16, 2022 at 05:56:13PM +1200, Greg Ewing wrote:
> On 16/04/22 12:56 pm, Steven D'Aprano wrote:
> >If you exclude models of MI which are logically incoherent and
> >inconsistent, (such as Python's prior to version 2.3), then Python's
> >model of MI is objectively as complete as you can get.
> 
> You seem to be assuming that "full MI" has to include a fully
> automatic means of method resolution.

How very observant of you, I only mentioned that about a hundred 
bazillion times so far!!!

(Yes, I am frustrated. But cheerfully so.)

> There's nothing incoherent or inconsistent about the way C++
> and Eiffel do MI.

Good thing I never said that they were incoherent or inconsistent.

I'm happy to have vigorous debate, but it would be kinda nice if people 
would argue against my actual position.

As I understand it, C++ and Eiffel still do automatic method resolution, 
it is only when there is a conflict that they demand you manually 
resolve the conflict.


> The main difference is that they require you
> to explicitly resolve conflicts between inherited methods --

Right, another thing which I have mentioned. In that regard, they 
are sort of like traits, as developed in Squeak.

(Scala traits are something different. Another example of different 
languages using the same term to mean different things.)


> which is arguably more Pythonic than Python, since they refuse
> the temptation to guess.

There is no *guessing* in the C3 linearization algorithm.



-- 
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/MJNOGXMDLBVBPGMOJ624AOAJHQ3C3TJJ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Conditions for a coherent MI relationship [was Re: Re: mro and super don't feel so pythonic]

2022-04-16 Thread Steven D'Aprano
On Sat, Apr 16, 2022 at 04:40:58PM +1000, Chris Angelico wrote:

> > Which conditions would you drop? There's not that many, really. Five.
> > Six if you include the "no cycles" requirement for the DAG, which I
> > think is so obviously necessary that it is barely worth mentioning.
> 
> This is *exactly* the "no true Scotsman" fallacy: you have already
> excluded from consideration anything that drops a condition you didn't
> already drop.

No, I have excluded them from consideration because if you allow them, 
**inheritance doesn't work properly**.

It becomes inconsistent and buggy. Bad things happen, like the method 
resolution order depending on how you spell the class name, or differing 
between a class and its subclass.

Maybe those bad things will be rare. MI in Python 1.x only misbehaved if 
you had a diamond graph, which was rare with classic classes. With 
new-style classes, all multiple inheritance includes diamonds, and MI 
in Python 2.2 misbehaved under some circumstances but not all:

https://mail.python.org/pipermail/python-dev/2002-October/029035.html

For 2.3, we swapped to using a proven algorithm, C3 linearization, to 
fix those problems. This was not an arbitrary choice. It is necessary to 
avoid inheritance misbehaving.

Like Dylan, Ruby, Perl etc (to the best of my knowledge, corrections are 
welcome) Python now supports as many cases of automatic inheritance in 
MI as it is possible to support without breakage.

There is no more silent breakage in the MI model like there was in 
Python 1.5, instead we get a clear exception if we try to create an 
inconsistent class hierarchy.

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.

As I have said now more times than I can count, that's a perfectly 
acceptible design choice, maybe even better than Python's, but it means 
that their MI model is less general than Python's.

Hard to believe that this mild take is so controversial. Imagine if I 
had something really wild like "Python lists are mutable" or "cars 
generally drive on four wheels".


> On the assumption that your five conditions are
> essential, there's no way that you can drop any of the five conditions
> and still have it count, therefore the five conditions are essential.
> Your logic is circular.

You seem to be determined to accuse me of every fallacy under the sun, 
whether it applies or not. Excluded middle, No True Scotsman (no matter 
how many times I say that other choices for MI are legitimate and maybe 
even better than Python's choice), circular reasoning.

At least you haven't (yet) accused me of poisoning the well, perhaps 
because the irony would be too much.

The conditions I have given are not essential because Python has them, 
but because they genuinely are essential to avoid buggy MI like Python 
used to have.

There are other ways to avoid such bugs. You can do what Java does, and 
not allow MI at all. Or you can refuse to resolve method conflicts, like 
C++ and Eiffel. Or you might just hope that nobody notices the bugs, and 
if they do, close them as Won't Fix. There are many strategies a 
language might take.


> It is highly arrogant to assume that nobody will ever find a way to
> implement MI while dropping one of your conditions. They're not
> fundamental to the definition, they're fundamental to *the way Python
> does things*.

No, they are fundamental to the definition. People just don't generally 
mention them, either because they don't know them, or take them for 
granted.

The three conditions for C3 are necessary for MI to be 
consistent and coherent. Of course you don't need them if you have 
subclassing without inheritance (like mixins in some languages), or 
no MI at all, but otherwise you need C3.

The rule that the linearization should only depend on the DAG between 
classes, and not on incidental factors like their name, or the time of 
the day, or a random number generator, is just common sense.

https://i.imgur.com/fIVQIj8.jpg

The requirement for automatic conflict resolution is kinda necessary for 
it to be inheritance, otherwise you're doing something else. E.g. in 
Eiffel, you have to rename the conflicting methods. In C++ you have to 
use explicit delegation.

Which is cool. As I have said about a bajillion times now, there are 
good arguments that the more restrictive models of MI implemented by C++ 
and Eiffel are better than the less restrictive model used by Python.

So please stop falsely accusing me of "No True Scotsman" bullshit. If 
you keep doing it, I will know you're not arguing in good faith.


> > The most subjective is the requirement for automatic conflict
> > resolution. It is a legitimate design choice to give up automatic
> > conflict resolution (that's what C++ and Eiffel do) but that would be a
> > breaking change for Python.
> 
>