On 2020-02-14 11:42 p.m., Steven D'Aprano wrote:
On Fri, Feb 14, 2020 at 07:24:48PM -0300, Soni L. wrote:
In Rust, you can have:
That's great for people who understand both Rust styntax and Rust semantics, but this is a Python discussion group, not Rust. We aren't all Rust experts.
I do suggest learning Rust, at least for the traits.
with strait, you can't have both Foo and Bar on Baz - it just raises by default. if you don't want it to raise, you have to specify an x,
That's how traits are (usually) defined: if there is an ambiguity due to conflicting methods, you have to explicitly resolve the ambiguity, or else it's an error.
If the compiler just picks one for you, you don't have traits, you have multiple inheritence.
(Above, I say "usually", because there are many subtle or not-so-subtle differences between the way traits are defined in different languages.)
That's how mixins-with-extra-steps are defined.
at which point it behaves like mixins,
Well, yes. Mixins with no conflicting methods is just the same as traits. The difference is that traits make you resolve conflicts explicitly, rather than just using some default rule.
No, it isn't.
not giving you the niceties of Rust traits.
for proper traits, you need to be able to select, implicitly or explicitly, which trait impl you want to call.
Right. And that's what the strait module allows too. See the example:
I think that if this discussion is going to become productive, you need to start by defining what "traits" mean to you, in detail. What do they do? What are their semantics? Focus on how they differ from multiple inheritence and mixins in Python, and what problems you would solve by using them.
Entities and features going by the name "trait" occur in many languages, but have different meanings. Unless we agree on a meaning, we are just talking past each other.
You should give *short*, *simple* but *concrete* examples, in pseudocode that demonstrates the principle, not the mechanical details of some specific interface. Interfaces can be changed. The semantics of the feature is what is important for understanding.
Using abstract metasyntactic variables like spam, eggs, foo, bar, x, y that do nothing and mean nothing is not going to be helpful. Use a concrete, actual example, but simplifed.
Okay. To put it simple: traits and composition aren't the same thing. If they were, we'd call them composition rather than invent a new name. They aren't mixins either.
They're namespaced interfaces. The namespace is the interface itself. You can apply multiple such interfaces to the same type, each having all the same names, and when you pass the type around, stuff just works. That's the *WHOLE* point of traits.
Here's a table for you:
- Composition: - Components can be added to an object - Components provide an interface and an implementation of that interface - Components don't conflict - This is how so-called "ECS" works - Mixins: - Mixins can have their names injected into an object - Name resolution order is generally automatically decided by the compiler/runtime, but there's nothing that prevents you from having mixins with explicit resolution (or error on conflicts). - Mixins may conflict. This is a pain. - Mixins don't inherently provide an interface, just an implementation of one. - Nobody likes mixins. They're just multiple inheritance with extra steps. - Traits: - Traits define an interface. They don't inject anything anywhere, and don't get added anywhere either. - An object can implement a trait, but your object is yours and the trait won't interfere with it. - There is no such thing as name resolution order. (* some languages have default methods/etc, which is arguably a form of name resolution order.) - Generally, implementations of traits will provide some convenience where non-conflicting names will resolve to a given trait. these may or may not be considered part of your public API. Implementations that do this will generally distinguish "inherent" methods, defined directly on a type, from... "non-inherent" (? idk the name for this) methods, defined on a trait. - It's not mixins with extra steps. It's not even composition with extra steps, altho it comes a lot closer to composition than to mixins. It's more like an attempt to combine the best of mixins with the best of composition, if anything.
Now, let's take an example from strait:
class TOSWidget(BaseWidget): __metaclass__ = include(Pack, Place, Grid) info = Pack.info.im_func config = Pack.config.im_func configure = Pack.configure.im_func slaves = Pack.slaves.im_func forget = Pack.forget.im_func propagate = Pack.propagate.im_func
Where does TOSWidget implement Pack.info? Place.info? Pack.config? Place.config? etc? Oh, yeah, it doesn't, because strait is just mixins. It calls itself "trait" just to create unnecessary confusion. If you tried to pass this TOSWidget to something trying to interact with a Place (assuming Place and Pack use the same names for different things), it just wouldn't work, as it's using the Pack methods where Place methods are expected!