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.

 > > > 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:

 > 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."

 > 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.

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.

 > 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.

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. :-(

 >    - Generally, implementations of traits will provide some
 >      convenience where non-conflicting names will resolve to a
 >      given trait.

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.

 > 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.

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.

 > 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!

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.

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

Reply via email to