
I'd like to see traits some day, with a syntax similar to this one: trait Trait: def x(self): raise NotImplementedError def y(self): raise NotImplementedError trait Anoher: def x(self): raise NotImplementedError def y(self): raise NotImplementedError def foo(Trait(x)): x.x() class Bar: def y(self): print("hello") impl Trait: def x(self): self.y() # resolves to Bar.y impl Another: def x(self): raise ValueError foo(Bar()) # prints 'hello' Trait(Bar()).x() # also prints 'hello' Bar().x() # AttributeError: ambiguous reference to trait method x if the trait isn't used in the function definition you get the raw object, where name conflicts between traits (but not between traits and inherent methods) result in an error about name conflicts. otherwise you get a friendly wrapper.

On Fri, Feb 7, 2020 at 10:11 AM Soni L. <fakedme+py@gmail.com> wrote:
I assume traits are a feature of another language, but not being familiar with it can you illustrate its need a bit better? Can you give an example in current Python, and how it could be made more clear with the notional trait syntax? Nick

Best reference that I know of: "Traits: A Mechanism for Fine-grained Reuse" by: STEPHANE DUCASSE OSCAR NIERSTRASZ and NATHANAEL SCHARLI ROEL WUYTS ANDREW P. BLACK *Inheritance is well-known and accepted as a mechanism for reuse in object-oriented languages. Unfortunately, due to the coarse granularity of inheritance, it may be difficult to decompose an application into an optimal class hierarchy that maximizes software reuse. Existing schemes based on single inheritance, multiple inheritance, or mixins, all pose numerous problems for reuse. To overcome these problems we propose traits, pure units of reuse consisting only of methods. We develop a formal model of traits that establishes how traits can be composed, either to form other traits, or to form classes. We also outline an experimental validation in which we apply traits to refactor a non-trivial application into composable units.* Note that there are several packages already in PyPI: - https://pypi.org/project/traits/ - https://pypi.org/project/strait/ (Never used them myself). S. On Fri, Feb 7, 2020 at 6:04 PM Soni L. <fakedme+py@gmail.com> wrote:
-- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyParis & PyData Paris - http://pyparis.org/ & http://pydata.fr/

Stéfane Fermigier wrote:
That first package is unrelated, I'm afraid: completely different meaning of the word "trait". In that case, a "trait" is an observable, typed attribute on a class. A class using traited attributes is a bit like a dataclass or an `attrs`-using class, but with the idea of notification built in: arbitrary listeners can be attached to react to changes in the attribute value. It's useful for data-driven and event-driven programming, and building scientific data models that you later want to build UIs around. Making `trait` a keyword would be an excellent way to give Enthought developers a major headache. :-) Mark

On Sat, Feb 8, 2020 at 4:01 AM Soni L. <fakedme+py@gmail.com> wrote:
Hello Nick!
Traits are an alternative to Multiple Inheritance. They solve the problem of name conflicts by making them an ambiguity error and requiring you to disambiguate (at call site).
Okay, that sounds like a good summary. I'm not sure how useful this truly will be, given that name conflicts in multiple inheritance usually mean you're overusing MI, but sure. Let's see how far we can go with current syntax. @do_we_need_a_class_deco_too class Trait: @trait def x(self): ... @trait def y(self): ... @ditto class Another: @trait def x(self): ... @trait def y(self): ... class Bar: def y(self): print("hello") @Trait.x def x(self): self.y() # resolves to Bar.y @Another.x: def x(self): raise ValueError All the real work would be done in the @trait decorator. (There might need to be a decorator on the class too, or maybe it subclasses something.) Trait.x would then also be a decorator, and the function it decorates gets stashed into a hidden attribute such as "__traits" (meaning that they're actually "_Trait__traits" and "_Another__traits" and don't conflict), and it would return a stub that raises an error if there's a conflict. When you call Trait(obj), it would return a proxy object (think like super()) that knows which set of traits to look up; most of the magic would be done by the __traits name mangling, so it'd be pretty easy. You wouldn't be able to have magic in the function header to do this automatically, though. How essential is that to the proposal? ChrisA

On Fri, Feb 7, 2020 at 12:02 PM Soni L. <fakedme+py@gmail.com> wrote:
The examples shown seem to create the very problem you claim traits solve. It is only the introduction of your `impl` keyword in the example that allowed there to be more than one `x()` in the first place. Can you elaborate on why this is even a problem in the first place that would need solved in Python code today?
-- CALVIN SPEALMAN SENIOR QUALITY ENGINEER cspealma@redhat.com M: +1.336.210.5107 TRIED. TESTED. TRUSTED.

On Fri, Feb 07, 2020 at 10:33:00AM -0600, Nick Timkovich wrote:
A decade ago, Michele Simionato wrote an excellent, highly detailed series of blog posts about super, multiple inheritance, mixins and traits. A good starting point is this post: https://www.artima.com/weblogs/viewpost.jsp?thread=246488 Traits are a more restrictive (and therefore safer) version of mixins, which are in turn a more restrictive (and therefore safer) version of multiple inheritance. See Michele's blog posts for more detail. -- Steven

You may be interested in the excellent traitlets library: https://traitlets.readthedocs.io/en/stable/ On Friday, February 7, 2020 at 11:11:59 AM UTC-5, Soni L. wrote:

That's not traits. That's its own thing. That's not even mixins, it just seems to be type-checked attributes. Nobody has implemented actual traits in Python yet, only mixins with extra steps and there are 2 libraries providing these type-checked attributes and calling them "traits" for whatever reason (they're not). On 2020-02-14 5:50 a.m., Neil Girdhar wrote:

On 02/14/2020 04:53 AM, Soni L. wrote:
That's not traits. That's its own thing. That's not even mixins, it just seems to be type-checked attributes. Nobody has implemented actual traits in Python yet, only mixins with extra steps and there are 2 libraries providing these type-checked attributes and calling them "traits" for whatever reason (they're not).
Probably because the word "traits" can be applied to more than one thing: - a person's traits - a family's traits - a language's traits - an attribute's traits - a class' traits - etc., etc. -- ~Ethan~

On Fri, Feb 14, 2020 at 09:53:27AM -0300, Soni L. wrote:
Nobody has implemented actual traits in Python yet, only mixins with extra steps
That's a bold claim, since the meaning of "traits" you gave earlier in the thread sounded to me exactly like "mixins with extra steps". Michele Simionato wrote what is (in my opinion) *the* definitive series of blog posts on super, multiple inheritance, mixins and traits https://www.artima.com/weblogs/viewpost.jsp?thread=246488 and a library claiming to implement traits: https://pypi.org/project/strait/ Unfortunately some of the links to external resources are now dead. However he seems to be basing his concept of traits on the paper which introduced the term: http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf If this is not what you mean by "traits", you ought to describe *precisely* what the word means to you and how they differ from Michele Simionato's library. -- Steven

In Rust, you can have: trait Foo { fn x(&self); } trait Bar { fn x(&self); } struct Baz {} impl Foo for Baz { fn x(&self) { } } impl Bar for Baz { fn x(&self) { } } 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, at which point it behaves like mixins, 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. in Rust, this is done in two (three) ways: 1. function has a bound on a trait: (not relevant for python or other dynamically/duck typed languages(!), is the most common way of doing it in Rust) fn foo<T: Bar>(obj: T) {...} 2. function takes a trait object, basically a wrapper object around the real object: fn foo(obj: &Bar) {...} 3. function explicitly calls the trait method on the object: let obj: Baz = ...; <Baz as Bar>::x(obj); in my python-based traits library, these become, respectively from the start: the trait, struct and impl definitions: class Foo(Trait): def x(self): raise NotImplementedError class Bar(Trait): def x(self): raise NotImplementedError class Baz(TraitObject): # "mirrors" class Baz(object): @impl(Foo) class Foo: def x(self): pass @impl(Bar) class Bar: def x(self): pass "the function has a bound on a trait" intentionally doesn't have an equivalent. the function takes a trait object: def foo(obj): obj = Bar(obj) # or, ideally, as sugar for the above: # def foo(Bar(obj)): # pass the function explicitly calls the trait method on the object: obj = Baz(); Bar(obj).x() # or Baz.Bar.x(obj) if you know the name under which the trait impl is located and wanna use it rather than making a wrapper trait object. quick digression: I personally feel strongly that traits add to python whereas trait bounds would take away from python. more specifically trait bounds would take away python's dynamic/duck typing. as such I don't plan on implementing trait bounds. further, as evidence that I'm not screwing over python's dynamic/duck typing: you can easily make any object look like a TraitObject, and that's a deliberate design decision. it might not necessarily pass isinstance/issubclass tho (but then, neither do existing fake ducks). On 2020-02-14 6:33 p.m., Steven D'Aprano wrote:

On Sat, Feb 15, 2020 at 9:25 AM Soni L. <fakedme+py@gmail.com> wrote:
And this is the bit where I ask again: What is your proposal and how is it different from just having an object with that collection of methods, since all the trait methods apply only to their own trait's data anyway? In other words, how is this different from perfectly ordinary object composition? ChrisA

On 02/14/2020 02:24 PM, Soni L. wrote:
That looks like a lot of extra boiler-plate to me (aka busy-work, aka anti-Python). Can you give us a real example using real code? You say you've made a library -- show us the library being used in real life -- foo, bar, and baz is not explaining anything, particularly for those of us who have no knowledge of Rust, nor the time/desire to go learn it. -- ~Ethan~

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.
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.)
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.
Right. And that's what the strait module allows too. See the example: https://pypi.org/project/strait/ 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. Thank you. -- Steven

On 2020-02-14 11:42 p.m., Steven D'Aprano wrote:
I do suggest learning Rust, at least for the traits.
That's how mixins-with-extra-steps are defined.
No, it isn't.
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!

Soni L. writes:
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:
I do suggest learning Rust, at least for the traits.
That's inappropriate because it's placing responsibility on us to puzzle out what you mean, rather than on you to explain it.
That's how traits are (usually) defined:
That's how mixins-with-extra-steps are defined.
I just read Schärli et al. I won't say "usual" definition as Steven did (I don't know other languages' implementations), but clearly that paper's definition is an *important* definition. Schärli et al. clearly envisioned an equality relation on method implementations in traits. The obvious trivial implementation is identity. Using the dictionary of methods implementation for simplicity of exposition: Trait = dict def foo(self): pass def bar(self): pass A = Trait(x = foo) B = Trait(x = foo) C = Trait(x = bar) With Python's definition of equality of functions, you cannot add A and B to the same class without raising, but you can add A and C (under Schärli et al's definition). If Simionato's strait doesn't account for equality of implementations in this sense, I suppose it would be easy enough to add. Presumably we could "improve" the idea of "equality" (eg by comparing ASTs) if we really want to. But you seem to ignore this aspect of the original definition of "trait."
This is rude, unless you have registered a trademark in the name "trait". Quite clearly the folks in this thread know that much, and have some concept of the differences. What you need to do is to figure out and explain where your definition conflicts with others', and explain why yours is more faithful to the abstract idea of "trait." I think that last part is going to be a very heavy lift, because the Schärli, et al paper is very well reasoned, and they make very good points about why each of the features in their design for "traits" is appropriate in the context of comprehension and maintenance of code using traits. But that's what you need to do, since I greatly doubt you own that trademark.
They're namespaced interfaces. The namespace is the interface itself.
This is quite incompatible with Schärli et al's design. They clearly require a way to determine equality of *implementations* of a method, because they envision automatically detecting changes to those implementations during maintenance. The interface alone is insufficient.
I'd call that "incomprehensible", and even "impossible." Your words indicate that you think that trait = "interface that just works [= never conflicts? but you don't say] by pure fscking magic", but surely you don't mean that. What do you mean here? In any case, all of the concepts discussed here "just work when you pass types around" for values of "working" where errors are raised appropriately for that design. The problems are elsewhere, in whether they do what we expect, whether they are easy to understand, and whether the errors that are raised when we would find them most useful. You need to specify how traits succeed in those aspects where others fail.
Here's a table for you:
This discussion is poorly served by such a table. It would be much more useful if your table indicated whether various features are shared or unique in the different concepts.
- Traits: - Traits define an interface.
Of course.
They don't inject anything anywhere, and don't get added anywhere either.
Nicely solipsistic, but incomprehensible, unless you mean "trait == interface" (and even then, doesn't the interface get added to the class somehow?)
- An object can implement a trait, but your object is yours and the trait won't interfere with it.
Obviously if the object does the implementation, the trait won't affect it. But the main point of "composition" (by any definition, except where purely restricted to interface; I will continue to use scare quotes below since you provide your own definition of the word) is efficient code reuse: if you fix the component, you fix all objects using it too. Do you mean such maintenance is "interference"? I can't believe you do, but that's what your words seem to mean. :-(
This is true of all language support for "composition," however: it abbreviates access to an object component's attribute to access to the object's attribute with the same name. This has a unique definition exactly when only one component (including the object itself) provides the attribute. The differences among different designs for "composition" are all in conflict resolution.
Rude, and toward someone not even present in the discussion. Again you seem to identify the idea of "trait" with that of "interface", and exclude code reuse. But in Schärli et al's discussion, code reuse is an explicit goal of their design for traits. Simionato's blog is also implicitly aware of the code reuse motivation, though he doesn't emphasize it at all.
Isn't that what "conflict resolution" means here? The point of traits as defined by *Schärli et al* is more or less that the language support for traits obeys[1] - Flat is better than nested. - Readability counts. - In the face of ambiguity, refuse the temptation to guess. - If the implementation is hard to explain, it's a bad idea. - Namespaces are one honking great idea -- let's do more of those! while recognizing that in the case of composition of components - There should be one-- and preferably only one --obvious way to do it. is impossible to obey in general, where "in general" means "you're gonna run into special cases every day or so", thus the importance of "don't guess." What you seem to want is nested namespaces where attributes are resolved in the following order 1. a binding in the object's own namespace (including an explicit resolution of conflicting bindings in component namespaces) 2. a binding in a component namespace, unique among all component namespaces 3. (optionally) some default binding. plus 4. an attribute in a nested namespace can be explicitly accessed by "external" code. But then I'm not sure why you complain that TOSWidget doesn't behave like a Place when the definition explicitly states that it always behaves like a Pack when there's a conflict (which is #1 in the order of resolution I suggested above). One of the advantages of traits as advocated by Schärli et al is precisely that you can discover this by reading the definition of TOSWidget (even if you don't understand traits!) You don't need to compute MRO, you don't even need to examine the parents. That's why they require explicit resolution of conflicts. (Of course where trait methods are acquired implicitly, you'd have to examine parents to find out what they provide. But not here.) Regards, Footnotes: [1] These five koan are not at all independent when discussing traits.

you don't want me to be rude but when I literally explain rust traits you throw me an "that's not traits and fuck you for being rude/trying to get me to think". I'm just gonna say learn rust since you actively refuse to accept my explanation of rust traits. don't reply again until you've learned rust. On 2020-02-15 5:38 a.m., Stephen J. Turnbull wrote:

On Sat, Feb 15, 2020 at 10:39 PM Soni L. <fakedme+py@gmail.com> wrote:
Status quo wins a stalemate. Onus is on YOU to convince people that traits are (a) important and (b) impossible to do with current syntax. I can't speak for anyone else, but at the moment, you've convinced me that traits are just a different way of talking about something (but I don't even know what), and they're just "I wish I had this thing". In other words, you've thoroughly convinced me that this thread is not worth participating in. ChrisA

The OP's posts all seem to consist of: 1. This other thing is way better 2. I won't explain what it is, but Rust does it better 3. The other folks who have used Rust don't understand it either 4. You are all too dumb to understand this great thing 5. Python should change it's syntax to allow this thing I won't explain. I really can't see a point of discussing it. But thanks Steven for the pointer to a great article by Michele that I think I missed a decade ago. I did write a few metaclass articles with him even before that; Michele is quite a bright guy. On Sat, Feb 15, 2020, 7:11 AM Chris Angelico <rosuav@gmail.com> wrote:

Telling someone you are trying to convince to do something to go learn something else to "get on your level" as the youth would put it... is not how you get people to agree with you. To summarize this thread, as I see it so far * you asked about adding a feature with the name "traits" to the language. * it was pointed out that "traits", in context, has a meaning on computer science * Rust's implementation of traits is some order of magnitude different from the plutonic ideal of traits * Rust, not being python, has to have its concepts translated. Your version of the translation of the rust concept is further from the plutonic ideal of a trait than other users on this list. * You were asked to nail down a definition of what you are talking about I think I have the order of events here. I still haven't seen a clear definition of what you actually want to propose. I'm not convinced of what traits are, let alone there are worthwhile, and then that they would warrant changing the syntax of the language.

On Sat, 15 Feb 2020 at 12:17, Alex Walters <tritium-list@sdamon.com> wrote:
Having followed this thread, but not participated, I would go as far as to say that whether traits as a concept are something useful for Python, I would not be willing to accept a proposal on them from someone with the attitude that has been displayed by the OP. I have no confidence that he will consider the impact of their proposal on other users of Python who do not have his understanding, and as that's pretty key to any proposal getting accepted, the whole discussion seems unlikely to get anywhere. I'm happy to see a proposal for a traits mechanism in Python, just not from the Soni L (unless there's an extremely visible change in his attitude and approach). Paul

On 2020-02-15 10:06 a.m., Paul Moore wrote:
I'm a they. and at least I don't tell ppl they're wrong when they're trying to explain to me what they mean. but hey I'm glad there are still ppl trying to make others feel unwelcome on the python-ideas mailing list, gives me a reason to steer ppl away from python.
Paul

All: If you're not interested in the back and forth, read my parallel post with a different subject. Soni L. writes:
OK, done.
you don't want me to be rude
I don't actually care (cf. http://www.jwz.org/gruntle/rbarip.html), except that we've all agreed to the Code of Conduct, and I hate to see folks ignored, or worse, banned, for non-conformance.
but when I literally explain rust traits
So far, from what you've written, they seemed to be what in Python I would call "zope.interface interfaces." And in fact, both you and the 3 Rust-specific sources cited below emphasize this aspect of traits. Sadly, you did a very poor job of explaining what Rust traits are, and their advantages over strait. Possibly because there aren't any advantages that aren't strait implementation details. At least, I can't find any.
you throw me an "that's not traits"
No, I'm throwing you an "I don't understand what you want." I was not going to say "they're not traits" until I understood them. I told you what I understood them to be *from your descriptions*, which turned out to be incomplete in rather important ways. Now that I understand them better, they do seem to be traits à la Schärli et al., except that arbitrarily nested traits are allowed in Rust, and Rust allows unimplemented methods. (This is not a restriction in Schärli et al's version of traits: the implementation of a trait method can simply call a method provided by the class incorporating the trait.) In the TOSWidget example, as I understand it, in Rust: 1. 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. 2. 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. 3. If both Pack and Place implement 'config', but TOSWidget does not, you get a compile-time error. I still don't understand your comment implying that the fact that something that expects Place behavior will get Pack behavior from TOSWidget means strait is inferior to Rust's version. I don't see how Rust can do better that that -- any code that expects Place behavior from the 6 methods defined in TOSWidget is just plain incorrect code in Rust as well as in strait. I guess you're implicitly appealing to Rust's UFCS (universal function call syntax)? That's kind of unfair: it should be easy to implement a UFCS for strait traits, although it might need a clumsy syntax. As pointed out in Rust documentation and bug reports, UFCS is only rarely needed, so clumsy syntax is not such a problem.
"and fuck you for ..."
Not at all. I was mostly inviting you to correct my understanding by explaining the details of Rust's implementation and pointing out where it differs. I gather you aren't interested in trying, but I'm always happy to help.
"... trying to get me to think."
I put 2 hours into composing that post, and another 2 into this one. If that's your definition of "refusing to think," you're wrong. Good luck, anyway. Regards,

Hi folks, moderator here. I’m calling it for this discussion. No further discussion is called for, IMO, although I’m sure Brett and I would be happy to be overridden by private request. Stephen, thank for providing an excellent summary post on which to lay this thread to rest :) :) best, --titus

Executive summary: I'm sorry so many people have not gotten much out of the parent thread. Despite my disagreements with the OP, I find traits to be an interesting concept, and I'll probably try out Simionato's strait library[1]. I agree with several posters that traits neither need nor are important enough to deserve special syntax. In my previous post, I pointed out that traits conform to a slew of Zen that general multiple inheritance violates, and even mixins can only avoid with disciplined use. I think they're worth considering if you use mixins as they prevent foot-shooting in some reasonably frequent cases. Of course the flip side is that undisciplined use of mixins is not always going to put you on crutches, so YMMV. Some details follow, if I've piqued your interest. The point of traits as defined by Schärli et al is more or less that the support for traits respects the Zen: - Flat is better than nested. - Readability counts. - In the face of ambiguity, refuse the temptation to guess. - If the implementation is hard to explain, it's a bad idea. - Namespaces are one honking great idea -- let's do more of those! Unlike mixins, which are just a disciplined use of classes in Python, proper traits satisfy a "flattening" property: a class or trait composed from several traits behaves exactly the same as if all methods were defined directly in the class or composite trait, regardless of nesting of traits or order of addition. If you combine two traits, you get a new trait. You can still combine the new trait with either or both of the older ones, because the method implementations are the same. (I don't know if strait implements this check; it may do the easy thing and just check for name conflicts.) To extend this property to work with class inheritance, the only method resolution order (MRO) you need to know for traits is: a definition in a class overrides a definition in a trait used by the class which overrides a definition in a superclass of the class. Composite traits aren't actually flat, and so cost some readability, but at least you don't need to compute the MRO. Again, unlike mixins which invoke the MRO to disambiguate methods defined in multiple mixins in the class hierarchy, when multiple traits define methods for the same name, the class using those traits must "refuse the temptation to guess" (even if obvious!) and explicitly disambiguate. Traits improve "readability" by avoiding the MRO, which is "hard to explain." Everything you need to know about which methods will be invoked is in the class definition, although you may need to read trait documentation to know all the methods implemented. And as the OP points out, traits provide "namespaces" (so I guess we can count Uncle Tim as a vote for traits! ;-) The bottom line is that disciplined use of mixins likely provides most of the benefits of traits if that discipline includes a very flat class hierarchy and sufficient care in avoiding method name collisions. Finally, an aside on Simionato's blog post. He claims that although traits are a lot safer than general multiple inheritance or even mixins, generic functions are "better" than traits. I don't understand the interaction between generic functions and traits in Rust yet, but in that language they are evidently complementary features, not alternatives. Further reading: This tutorial on Rust (readable, although the examples are excessively verbose) http://gradebot.org/doc/ipur/trait.html indicates that in that language, interface definition is the primary purpose of a trait in (I don't take a tutorial as definitive, although the tutorial mirrors Soni's focus on interface), but traits are allowed to have default implementations of methods. zope.interface interfaces deliberately avoid that; they're "pure" interfaces (at least where I've used them). Nothing in any of the Rust documentation I looked at so far emphasizes code reuse or default implementations. I gather Rust also implements at least some "trait algebra," as Wikipedia describes: https://en.wikipedia.org/wiki/Trait_(computer_programming)#Characteristics After further research, I see that only "+" is implemented, and so far I have not been able to find documentation of whether it's disjoint or overriding. Soni's post implies that if there are multiple implementations, one will be chosen (but any others are still accessible to code that knows which trait it wants). See also Fisher and Reppy for an extended treatment of trait algebra http://newtraell.cs.uchicago.edu/files/tr_authentic/TR-2003-13.pdf which cites Schärli et al, but not vice versa. The Schärli et paper is here: http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf Footnotes: [1] Which I keep spelling as "Simionati", mea culpa. https://www.artima.com/weblogs/viewpost.jsp?thread=246488 -- Associate Professor Division of Policy and Planning Science http://turnbull/sk.tsukuba.ac.jp/ Faculty of Systems and Information Email: turnbull@sk.tsukuba.ac.jp University of Tsukuba Tel: 029-853-5175 Tennodai 1-1-1, Tsukuba 305-8573 JAPAN

It sounds like it would be a useful exercise to design a complete, modern Traits implementation for Python 3 (maybe starting with Simoniato's straits). An extra challenge would be to make it fit in the type system as perceived by PEP-484-based type checkers (mypy, pyre etc.) -- without this it would end up being a dynamically-typed dead end in Python's evolution. On Sat, Feb 15, 2020 at 9:23 AM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Fri, Feb 7, 2020 at 10:11 AM Soni L. <fakedme+py@gmail.com> wrote:
I assume traits are a feature of another language, but not being familiar with it can you illustrate its need a bit better? Can you give an example in current Python, and how it could be made more clear with the notional trait syntax? Nick

Best reference that I know of: "Traits: A Mechanism for Fine-grained Reuse" by: STEPHANE DUCASSE OSCAR NIERSTRASZ and NATHANAEL SCHARLI ROEL WUYTS ANDREW P. BLACK *Inheritance is well-known and accepted as a mechanism for reuse in object-oriented languages. Unfortunately, due to the coarse granularity of inheritance, it may be difficult to decompose an application into an optimal class hierarchy that maximizes software reuse. Existing schemes based on single inheritance, multiple inheritance, or mixins, all pose numerous problems for reuse. To overcome these problems we propose traits, pure units of reuse consisting only of methods. We develop a formal model of traits that establishes how traits can be composed, either to form other traits, or to form classes. We also outline an experimental validation in which we apply traits to refactor a non-trivial application into composable units.* Note that there are several packages already in PyPI: - https://pypi.org/project/traits/ - https://pypi.org/project/strait/ (Never used them myself). S. On Fri, Feb 7, 2020 at 6:04 PM Soni L. <fakedme+py@gmail.com> wrote:
-- Stefane Fermigier - http://fermigier.com/ - http://twitter.com/sfermigier - http://linkedin.com/in/sfermigier Founder & CEO, Abilian - Enterprise Social Software - http://www.abilian.com/ Chairman, National Council for Free & Open Source Software (CNLL) - http://cnll.fr/ Founder & Organiser, PyParis & PyData Paris - http://pyparis.org/ & http://pydata.fr/

Stéfane Fermigier wrote:
That first package is unrelated, I'm afraid: completely different meaning of the word "trait". In that case, a "trait" is an observable, typed attribute on a class. A class using traited attributes is a bit like a dataclass or an `attrs`-using class, but with the idea of notification built in: arbitrary listeners can be attached to react to changes in the attribute value. It's useful for data-driven and event-driven programming, and building scientific data models that you later want to build UIs around. Making `trait` a keyword would be an excellent way to give Enthought developers a major headache. :-) Mark

On Sat, Feb 8, 2020 at 4:01 AM Soni L. <fakedme+py@gmail.com> wrote:
Hello Nick!
Traits are an alternative to Multiple Inheritance. They solve the problem of name conflicts by making them an ambiguity error and requiring you to disambiguate (at call site).
Okay, that sounds like a good summary. I'm not sure how useful this truly will be, given that name conflicts in multiple inheritance usually mean you're overusing MI, but sure. Let's see how far we can go with current syntax. @do_we_need_a_class_deco_too class Trait: @trait def x(self): ... @trait def y(self): ... @ditto class Another: @trait def x(self): ... @trait def y(self): ... class Bar: def y(self): print("hello") @Trait.x def x(self): self.y() # resolves to Bar.y @Another.x: def x(self): raise ValueError All the real work would be done in the @trait decorator. (There might need to be a decorator on the class too, or maybe it subclasses something.) Trait.x would then also be a decorator, and the function it decorates gets stashed into a hidden attribute such as "__traits" (meaning that they're actually "_Trait__traits" and "_Another__traits" and don't conflict), and it would return a stub that raises an error if there's a conflict. When you call Trait(obj), it would return a proxy object (think like super()) that knows which set of traits to look up; most of the magic would be done by the __traits name mangling, so it'd be pretty easy. You wouldn't be able to have magic in the function header to do this automatically, though. How essential is that to the proposal? ChrisA

On Fri, Feb 7, 2020 at 12:02 PM Soni L. <fakedme+py@gmail.com> wrote:
The examples shown seem to create the very problem you claim traits solve. It is only the introduction of your `impl` keyword in the example that allowed there to be more than one `x()` in the first place. Can you elaborate on why this is even a problem in the first place that would need solved in Python code today?
-- CALVIN SPEALMAN SENIOR QUALITY ENGINEER cspealma@redhat.com M: +1.336.210.5107 TRIED. TESTED. TRUSTED.

On Fri, Feb 07, 2020 at 10:33:00AM -0600, Nick Timkovich wrote:
A decade ago, Michele Simionato wrote an excellent, highly detailed series of blog posts about super, multiple inheritance, mixins and traits. A good starting point is this post: https://www.artima.com/weblogs/viewpost.jsp?thread=246488 Traits are a more restrictive (and therefore safer) version of mixins, which are in turn a more restrictive (and therefore safer) version of multiple inheritance. See Michele's blog posts for more detail. -- Steven

You may be interested in the excellent traitlets library: https://traitlets.readthedocs.io/en/stable/ On Friday, February 7, 2020 at 11:11:59 AM UTC-5, Soni L. wrote:

That's not traits. That's its own thing. That's not even mixins, it just seems to be type-checked attributes. Nobody has implemented actual traits in Python yet, only mixins with extra steps and there are 2 libraries providing these type-checked attributes and calling them "traits" for whatever reason (they're not). On 2020-02-14 5:50 a.m., Neil Girdhar wrote:

On 02/14/2020 04:53 AM, Soni L. wrote:
That's not traits. That's its own thing. That's not even mixins, it just seems to be type-checked attributes. Nobody has implemented actual traits in Python yet, only mixins with extra steps and there are 2 libraries providing these type-checked attributes and calling them "traits" for whatever reason (they're not).
Probably because the word "traits" can be applied to more than one thing: - a person's traits - a family's traits - a language's traits - an attribute's traits - a class' traits - etc., etc. -- ~Ethan~

On Fri, Feb 14, 2020 at 09:53:27AM -0300, Soni L. wrote:
Nobody has implemented actual traits in Python yet, only mixins with extra steps
That's a bold claim, since the meaning of "traits" you gave earlier in the thread sounded to me exactly like "mixins with extra steps". Michele Simionato wrote what is (in my opinion) *the* definitive series of blog posts on super, multiple inheritance, mixins and traits https://www.artima.com/weblogs/viewpost.jsp?thread=246488 and a library claiming to implement traits: https://pypi.org/project/strait/ Unfortunately some of the links to external resources are now dead. However he seems to be basing his concept of traits on the paper which introduced the term: http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf If this is not what you mean by "traits", you ought to describe *precisely* what the word means to you and how they differ from Michele Simionato's library. -- Steven

In Rust, you can have: trait Foo { fn x(&self); } trait Bar { fn x(&self); } struct Baz {} impl Foo for Baz { fn x(&self) { } } impl Bar for Baz { fn x(&self) { } } 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, at which point it behaves like mixins, 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. in Rust, this is done in two (three) ways: 1. function has a bound on a trait: (not relevant for python or other dynamically/duck typed languages(!), is the most common way of doing it in Rust) fn foo<T: Bar>(obj: T) {...} 2. function takes a trait object, basically a wrapper object around the real object: fn foo(obj: &Bar) {...} 3. function explicitly calls the trait method on the object: let obj: Baz = ...; <Baz as Bar>::x(obj); in my python-based traits library, these become, respectively from the start: the trait, struct and impl definitions: class Foo(Trait): def x(self): raise NotImplementedError class Bar(Trait): def x(self): raise NotImplementedError class Baz(TraitObject): # "mirrors" class Baz(object): @impl(Foo) class Foo: def x(self): pass @impl(Bar) class Bar: def x(self): pass "the function has a bound on a trait" intentionally doesn't have an equivalent. the function takes a trait object: def foo(obj): obj = Bar(obj) # or, ideally, as sugar for the above: # def foo(Bar(obj)): # pass the function explicitly calls the trait method on the object: obj = Baz(); Bar(obj).x() # or Baz.Bar.x(obj) if you know the name under which the trait impl is located and wanna use it rather than making a wrapper trait object. quick digression: I personally feel strongly that traits add to python whereas trait bounds would take away from python. more specifically trait bounds would take away python's dynamic/duck typing. as such I don't plan on implementing trait bounds. further, as evidence that I'm not screwing over python's dynamic/duck typing: you can easily make any object look like a TraitObject, and that's a deliberate design decision. it might not necessarily pass isinstance/issubclass tho (but then, neither do existing fake ducks). On 2020-02-14 6:33 p.m., Steven D'Aprano wrote:

On Sat, Feb 15, 2020 at 9:25 AM Soni L. <fakedme+py@gmail.com> wrote:
And this is the bit where I ask again: What is your proposal and how is it different from just having an object with that collection of methods, since all the trait methods apply only to their own trait's data anyway? In other words, how is this different from perfectly ordinary object composition? ChrisA

On 02/14/2020 02:24 PM, Soni L. wrote:
That looks like a lot of extra boiler-plate to me (aka busy-work, aka anti-Python). Can you give us a real example using real code? You say you've made a library -- show us the library being used in real life -- foo, bar, and baz is not explaining anything, particularly for those of us who have no knowledge of Rust, nor the time/desire to go learn it. -- ~Ethan~

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.
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.)
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.
Right. And that's what the strait module allows too. See the example: https://pypi.org/project/strait/ 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. Thank you. -- Steven

On 2020-02-14 11:42 p.m., Steven D'Aprano wrote:
I do suggest learning Rust, at least for the traits.
That's how mixins-with-extra-steps are defined.
No, it isn't.
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!

Soni L. writes:
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:
I do suggest learning Rust, at least for the traits.
That's inappropriate because it's placing responsibility on us to puzzle out what you mean, rather than on you to explain it.
That's how traits are (usually) defined:
That's how mixins-with-extra-steps are defined.
I just read Schärli et al. I won't say "usual" definition as Steven did (I don't know other languages' implementations), but clearly that paper's definition is an *important* definition. Schärli et al. clearly envisioned an equality relation on method implementations in traits. The obvious trivial implementation is identity. Using the dictionary of methods implementation for simplicity of exposition: Trait = dict def foo(self): pass def bar(self): pass A = Trait(x = foo) B = Trait(x = foo) C = Trait(x = bar) With Python's definition of equality of functions, you cannot add A and B to the same class without raising, but you can add A and C (under Schärli et al's definition). If Simionato's strait doesn't account for equality of implementations in this sense, I suppose it would be easy enough to add. Presumably we could "improve" the idea of "equality" (eg by comparing ASTs) if we really want to. But you seem to ignore this aspect of the original definition of "trait."
This is rude, unless you have registered a trademark in the name "trait". Quite clearly the folks in this thread know that much, and have some concept of the differences. What you need to do is to figure out and explain where your definition conflicts with others', and explain why yours is more faithful to the abstract idea of "trait." I think that last part is going to be a very heavy lift, because the Schärli, et al paper is very well reasoned, and they make very good points about why each of the features in their design for "traits" is appropriate in the context of comprehension and maintenance of code using traits. But that's what you need to do, since I greatly doubt you own that trademark.
They're namespaced interfaces. The namespace is the interface itself.
This is quite incompatible with Schärli et al's design. They clearly require a way to determine equality of *implementations* of a method, because they envision automatically detecting changes to those implementations during maintenance. The interface alone is insufficient.
I'd call that "incomprehensible", and even "impossible." Your words indicate that you think that trait = "interface that just works [= never conflicts? but you don't say] by pure fscking magic", but surely you don't mean that. What do you mean here? In any case, all of the concepts discussed here "just work when you pass types around" for values of "working" where errors are raised appropriately for that design. The problems are elsewhere, in whether they do what we expect, whether they are easy to understand, and whether the errors that are raised when we would find them most useful. You need to specify how traits succeed in those aspects where others fail.
Here's a table for you:
This discussion is poorly served by such a table. It would be much more useful if your table indicated whether various features are shared or unique in the different concepts.
- Traits: - Traits define an interface.
Of course.
They don't inject anything anywhere, and don't get added anywhere either.
Nicely solipsistic, but incomprehensible, unless you mean "trait == interface" (and even then, doesn't the interface get added to the class somehow?)
- An object can implement a trait, but your object is yours and the trait won't interfere with it.
Obviously if the object does the implementation, the trait won't affect it. But the main point of "composition" (by any definition, except where purely restricted to interface; I will continue to use scare quotes below since you provide your own definition of the word) is efficient code reuse: if you fix the component, you fix all objects using it too. Do you mean such maintenance is "interference"? I can't believe you do, but that's what your words seem to mean. :-(
This is true of all language support for "composition," however: it abbreviates access to an object component's attribute to access to the object's attribute with the same name. This has a unique definition exactly when only one component (including the object itself) provides the attribute. The differences among different designs for "composition" are all in conflict resolution.
Rude, and toward someone not even present in the discussion. Again you seem to identify the idea of "trait" with that of "interface", and exclude code reuse. But in Schärli et al's discussion, code reuse is an explicit goal of their design for traits. Simionato's blog is also implicitly aware of the code reuse motivation, though he doesn't emphasize it at all.
Isn't that what "conflict resolution" means here? The point of traits as defined by *Schärli et al* is more or less that the language support for traits obeys[1] - Flat is better than nested. - Readability counts. - In the face of ambiguity, refuse the temptation to guess. - If the implementation is hard to explain, it's a bad idea. - Namespaces are one honking great idea -- let's do more of those! while recognizing that in the case of composition of components - There should be one-- and preferably only one --obvious way to do it. is impossible to obey in general, where "in general" means "you're gonna run into special cases every day or so", thus the importance of "don't guess." What you seem to want is nested namespaces where attributes are resolved in the following order 1. a binding in the object's own namespace (including an explicit resolution of conflicting bindings in component namespaces) 2. a binding in a component namespace, unique among all component namespaces 3. (optionally) some default binding. plus 4. an attribute in a nested namespace can be explicitly accessed by "external" code. But then I'm not sure why you complain that TOSWidget doesn't behave like a Place when the definition explicitly states that it always behaves like a Pack when there's a conflict (which is #1 in the order of resolution I suggested above). One of the advantages of traits as advocated by Schärli et al is precisely that you can discover this by reading the definition of TOSWidget (even if you don't understand traits!) You don't need to compute MRO, you don't even need to examine the parents. That's why they require explicit resolution of conflicts. (Of course where trait methods are acquired implicitly, you'd have to examine parents to find out what they provide. But not here.) Regards, Footnotes: [1] These five koan are not at all independent when discussing traits.

you don't want me to be rude but when I literally explain rust traits you throw me an "that's not traits and fuck you for being rude/trying to get me to think". I'm just gonna say learn rust since you actively refuse to accept my explanation of rust traits. don't reply again until you've learned rust. On 2020-02-15 5:38 a.m., Stephen J. Turnbull wrote:

On Sat, Feb 15, 2020 at 10:39 PM Soni L. <fakedme+py@gmail.com> wrote:
Status quo wins a stalemate. Onus is on YOU to convince people that traits are (a) important and (b) impossible to do with current syntax. I can't speak for anyone else, but at the moment, you've convinced me that traits are just a different way of talking about something (but I don't even know what), and they're just "I wish I had this thing". In other words, you've thoroughly convinced me that this thread is not worth participating in. ChrisA

The OP's posts all seem to consist of: 1. This other thing is way better 2. I won't explain what it is, but Rust does it better 3. The other folks who have used Rust don't understand it either 4. You are all too dumb to understand this great thing 5. Python should change it's syntax to allow this thing I won't explain. I really can't see a point of discussing it. But thanks Steven for the pointer to a great article by Michele that I think I missed a decade ago. I did write a few metaclass articles with him even before that; Michele is quite a bright guy. On Sat, Feb 15, 2020, 7:11 AM Chris Angelico <rosuav@gmail.com> wrote:

Telling someone you are trying to convince to do something to go learn something else to "get on your level" as the youth would put it... is not how you get people to agree with you. To summarize this thread, as I see it so far * you asked about adding a feature with the name "traits" to the language. * it was pointed out that "traits", in context, has a meaning on computer science * Rust's implementation of traits is some order of magnitude different from the plutonic ideal of traits * Rust, not being python, has to have its concepts translated. Your version of the translation of the rust concept is further from the plutonic ideal of a trait than other users on this list. * You were asked to nail down a definition of what you are talking about I think I have the order of events here. I still haven't seen a clear definition of what you actually want to propose. I'm not convinced of what traits are, let alone there are worthwhile, and then that they would warrant changing the syntax of the language.

On Sat, 15 Feb 2020 at 12:17, Alex Walters <tritium-list@sdamon.com> wrote:
Having followed this thread, but not participated, I would go as far as to say that whether traits as a concept are something useful for Python, I would not be willing to accept a proposal on them from someone with the attitude that has been displayed by the OP. I have no confidence that he will consider the impact of their proposal on other users of Python who do not have his understanding, and as that's pretty key to any proposal getting accepted, the whole discussion seems unlikely to get anywhere. I'm happy to see a proposal for a traits mechanism in Python, just not from the Soni L (unless there's an extremely visible change in his attitude and approach). Paul

On 2020-02-15 10:06 a.m., Paul Moore wrote:
I'm a they. and at least I don't tell ppl they're wrong when they're trying to explain to me what they mean. but hey I'm glad there are still ppl trying to make others feel unwelcome on the python-ideas mailing list, gives me a reason to steer ppl away from python.
Paul

All: If you're not interested in the back and forth, read my parallel post with a different subject. Soni L. writes:
OK, done.
you don't want me to be rude
I don't actually care (cf. http://www.jwz.org/gruntle/rbarip.html), except that we've all agreed to the Code of Conduct, and I hate to see folks ignored, or worse, banned, for non-conformance.
but when I literally explain rust traits
So far, from what you've written, they seemed to be what in Python I would call "zope.interface interfaces." And in fact, both you and the 3 Rust-specific sources cited below emphasize this aspect of traits. Sadly, you did a very poor job of explaining what Rust traits are, and their advantages over strait. Possibly because there aren't any advantages that aren't strait implementation details. At least, I can't find any.
you throw me an "that's not traits"
No, I'm throwing you an "I don't understand what you want." I was not going to say "they're not traits" until I understood them. I told you what I understood them to be *from your descriptions*, which turned out to be incomplete in rather important ways. Now that I understand them better, they do seem to be traits à la Schärli et al., except that arbitrarily nested traits are allowed in Rust, and Rust allows unimplemented methods. (This is not a restriction in Schärli et al's version of traits: the implementation of a trait method can simply call a method provided by the class incorporating the trait.) In the TOSWidget example, as I understand it, in Rust: 1. 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. 2. 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. 3. If both Pack and Place implement 'config', but TOSWidget does not, you get a compile-time error. I still don't understand your comment implying that the fact that something that expects Place behavior will get Pack behavior from TOSWidget means strait is inferior to Rust's version. I don't see how Rust can do better that that -- any code that expects Place behavior from the 6 methods defined in TOSWidget is just plain incorrect code in Rust as well as in strait. I guess you're implicitly appealing to Rust's UFCS (universal function call syntax)? That's kind of unfair: it should be easy to implement a UFCS for strait traits, although it might need a clumsy syntax. As pointed out in Rust documentation and bug reports, UFCS is only rarely needed, so clumsy syntax is not such a problem.
"and fuck you for ..."
Not at all. I was mostly inviting you to correct my understanding by explaining the details of Rust's implementation and pointing out where it differs. I gather you aren't interested in trying, but I'm always happy to help.
"... trying to get me to think."
I put 2 hours into composing that post, and another 2 into this one. If that's your definition of "refusing to think," you're wrong. Good luck, anyway. Regards,

Hi folks, moderator here. I’m calling it for this discussion. No further discussion is called for, IMO, although I’m sure Brett and I would be happy to be overridden by private request. Stephen, thank for providing an excellent summary post on which to lay this thread to rest :) :) best, --titus

Executive summary: I'm sorry so many people have not gotten much out of the parent thread. Despite my disagreements with the OP, I find traits to be an interesting concept, and I'll probably try out Simionato's strait library[1]. I agree with several posters that traits neither need nor are important enough to deserve special syntax. In my previous post, I pointed out that traits conform to a slew of Zen that general multiple inheritance violates, and even mixins can only avoid with disciplined use. I think they're worth considering if you use mixins as they prevent foot-shooting in some reasonably frequent cases. Of course the flip side is that undisciplined use of mixins is not always going to put you on crutches, so YMMV. Some details follow, if I've piqued your interest. The point of traits as defined by Schärli et al is more or less that the support for traits respects the Zen: - Flat is better than nested. - Readability counts. - In the face of ambiguity, refuse the temptation to guess. - If the implementation is hard to explain, it's a bad idea. - Namespaces are one honking great idea -- let's do more of those! Unlike mixins, which are just a disciplined use of classes in Python, proper traits satisfy a "flattening" property: a class or trait composed from several traits behaves exactly the same as if all methods were defined directly in the class or composite trait, regardless of nesting of traits or order of addition. If you combine two traits, you get a new trait. You can still combine the new trait with either or both of the older ones, because the method implementations are the same. (I don't know if strait implements this check; it may do the easy thing and just check for name conflicts.) To extend this property to work with class inheritance, the only method resolution order (MRO) you need to know for traits is: a definition in a class overrides a definition in a trait used by the class which overrides a definition in a superclass of the class. Composite traits aren't actually flat, and so cost some readability, but at least you don't need to compute the MRO. Again, unlike mixins which invoke the MRO to disambiguate methods defined in multiple mixins in the class hierarchy, when multiple traits define methods for the same name, the class using those traits must "refuse the temptation to guess" (even if obvious!) and explicitly disambiguate. Traits improve "readability" by avoiding the MRO, which is "hard to explain." Everything you need to know about which methods will be invoked is in the class definition, although you may need to read trait documentation to know all the methods implemented. And as the OP points out, traits provide "namespaces" (so I guess we can count Uncle Tim as a vote for traits! ;-) The bottom line is that disciplined use of mixins likely provides most of the benefits of traits if that discipline includes a very flat class hierarchy and sufficient care in avoiding method name collisions. Finally, an aside on Simionato's blog post. He claims that although traits are a lot safer than general multiple inheritance or even mixins, generic functions are "better" than traits. I don't understand the interaction between generic functions and traits in Rust yet, but in that language they are evidently complementary features, not alternatives. Further reading: This tutorial on Rust (readable, although the examples are excessively verbose) http://gradebot.org/doc/ipur/trait.html indicates that in that language, interface definition is the primary purpose of a trait in (I don't take a tutorial as definitive, although the tutorial mirrors Soni's focus on interface), but traits are allowed to have default implementations of methods. zope.interface interfaces deliberately avoid that; they're "pure" interfaces (at least where I've used them). Nothing in any of the Rust documentation I looked at so far emphasizes code reuse or default implementations. I gather Rust also implements at least some "trait algebra," as Wikipedia describes: https://en.wikipedia.org/wiki/Trait_(computer_programming)#Characteristics After further research, I see that only "+" is implemented, and so far I have not been able to find documentation of whether it's disjoint or overriding. Soni's post implies that if there are multiple implementations, one will be chosen (but any others are still accessible to code that knows which trait it wants). See also Fisher and Reppy for an extended treatment of trait algebra http://newtraell.cs.uchicago.edu/files/tr_authentic/TR-2003-13.pdf which cites Schärli et al, but not vice versa. The Schärli et paper is here: http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf Footnotes: [1] Which I keep spelling as "Simionati", mea culpa. https://www.artima.com/weblogs/viewpost.jsp?thread=246488 -- Associate Professor Division of Policy and Planning Science http://turnbull/sk.tsukuba.ac.jp/ Faculty of Systems and Information Email: turnbull@sk.tsukuba.ac.jp University of Tsukuba Tel: 029-853-5175 Tennodai 1-1-1, Tsukuba 305-8573 JAPAN

It sounds like it would be a useful exercise to design a complete, modern Traits implementation for Python 3 (maybe starting with Simoniato's straits). An extra challenge would be to make it fit in the type system as perceived by PEP-484-based type checkers (mypy, pyre etc.) -- without this it would end up being a dynamically-typed dead end in Python's evolution. On Sat, Feb 15, 2020 at 9:23 AM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>
participants (16)
-
Alex Walters
-
C. Titus Brown
-
Calvin Spealman
-
Chris Angelico
-
David Mertz
-
Ethan Furman
-
Greg Ewing
-
Guido van Rossum
-
Mark Dickinson
-
Neil Girdhar
-
Nick Timkovich
-
Paul Moore
-
Soni L.
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Stéfane Fermigier