Answering off-list by moderator request.
Before I go on to respond to the details, let me start by saying I'm definitely beginning to understand why you like the Rust syntax for traits, and maybe even some feeling for the semantics, although they're not very well documented anywhere I found in a couple hours of Googling and browsing. On the other hand, I don't think some of Rust's semantics are a good fit for Python (and go into that below).
Soni L. writes:
zope.interface appears to be more based on java interfaces than rust traits? I could be wrong tho.
zope.interface precedes Rust traits by at least two decades. I don't know what the relationship is between zope.interface and Java's interface is. Java was released in 1995, Zope in 1998, but Zope's predecessor, the "Bobo object publisher," was released to the public in the early 1990s. Of course interface checking in a very primitive form (function prototypes) go back to C++'s predecessor "C with Classes" in 1979, and I imagine Lisp has had them even longer, and in more elaborated form. C++ got virtual functions in 1985, which is much closer to the modern concept of separating interface from implementation, and abstract classes (classes containing one or more pure virtual functions) in 1989. So this stuff was in the air for several years before either Python or Java started development.
*forget* Schärli et al., this is too hard to explain if you keep expecting Schärli et al. behaviour
Not gonna happen. Thing is, I can hold two behaviors in my mind at the same time, but it would help if you paid some respect to the seminal paper that preceded Rust by more than a decade. At least call them "Rust traits". But that doesn't help much, since the Rust documents are really sparse, even about behavior, let alone about rationale for the behavior. I had to Google bug reports to find even a mention of method name collisions.
when Rust quite explicitly doesn't seem to follow that.
I now think you're wrong about Rust following Schärli et al., although it's impossible to be sure since there are differences, and the design decision rationales aren't to be found in the places I looked.
now everyone's been gaslighting me because I got fed up with this and idk what to do about that. but anyway,
"Gaslighting" is lying about something that both parties can see is false. That's not happening here. There's definitely a lot of confusion about what you've been talking about.
It's one thing to ask one person (such as me) to learn Rust; it's another to ask all the core developers to do so. It's reasonable for you to say that your time is worth as much as anybody else's, even Guido's (though I don't recommend saying *that* out loud!) It's not reasonable to say that your time is worth as much as everybody else's.
In the TOSWidget example, as I understand it, in Rust:
- In the trivial case where exactly one of TOSWidget, Pack, and Place implements 'config', you get that implementation, and any code that's not happy with that is incorrect.
Yes, but also not quite. Rust has a bit of implicitness here for ergonomics. If there's no conflict, it can easily figure out what you want/meant.
If there's no conflict, strait figures out what you want, too.
Also, it's not that Pack or Place implements 'config' - they *define* a 'config' interface. TOSWidget implements Pack.config and/or Place.config.
It can, but it doesn't necessarily do so. The traits can supply default implementations. For reasons I don't understand (but presume are valid), Rust emphasizes traits' role in providing interfaces over code-sharing.
- In the case TOSWidget implements 'config', and Pack or Place or both implements it, you get TOSWidget's implementation, but anything expecting Pack or Place behavior will not get it, and is incorrect.
No, anything expecting Pack behaviour will get Pack behaviour, anything expecting Place behaviour gets Place behaviour, and anything dealing with TOSWidget directly gets TOSWidget behaviour. They're effectively namespaced.
"Expect" here means what the author of the code that invokes a method on a TOSWidget object expects. Of course you can use UFCS to disambiguate, but that's not what most of us mean by "expecting", that's explicitly invoking the behavior. In Python, "expecting" means "duck typing (ie, a method invocation) does the right thing", but UFCS is effectively a cast, and uses normal function call syntax. In English, your use of that word is perfectly correct, but I guarantee using it that way in this context will totally confuse most Pythonistas.
- If both Pack and Place implement 'config', but TOSWidget does not, you get a compile-time error.
No, unless you try to interact with 'config' on a TOSWidget.
Of course. That's what most Python programmers expect. "Duck typing."
The error happens in the caller, not the implementer, and then you need to disambiguate, in the *caller*, which implementation you meant to call.
I really don't like that *for Python*. Rust can do it at low risk because it resolves the call at compile time. Python doesn't, so this means a runtime exception, possibly after I've spent many CPU seconds and hours of wall time on a computation. I suspect a lot of Pythonistas will feel the same way.
This can either be done by expecting Place or Pack explicitly (i.e. having Place or Pack in the function signature) or by explicitly specifying that you want to use TOSWidget's implementation of Place or Pack. In Rust, a TOSWidget can be both a Place and a Pack,
Literally being both is not possible in Python at all if there are method collisions because of the way multiple inheritance works. Python can't invoke a trait in the function signature, because Python functions don't have typed arguments. It would be possible in MyPy if you implemented traits as classes, but that doesn't help you at compile time, you'd have to add checking at runtime. So making this possible would be a major change in Python semantics, creating a new kind of data namespace below the class level. You won't get that in Python without convincing a lot of people that they can't live without true traits in some form, and then that emphasizing the same aspects of traits that Rust does is the right balance.
and things expecting Place behaviour expect it explicitly and get it explicitly, whereas things expecting Pack behaviour also expect it explicitly and get it explicitly.
If you have to be explicit, I don't see how this has a big advantage over having Place and Pack trait components as instance attributes, and a metaclass that automatically promotes trait methods to instance methods when there is no name collision.
I might've gone too far on this one, sorry.
Apology accepted. No offense taken.
Steven D'Aprano writes:
On Sun, Feb 16, 2020 at 06:08:54PM +0900, Stephen J. Turnbull wrote:
Answering off-list by moderator request.
Are you sure about that? :-)
I'm not sure about much of anything these days. I know how to do it, I just didn't. :-( My apologies to all.
Stephen J. Turnbull