Re: Class::Trait to the CPAN?
by Zaxo (Archbishop) on Feb 16, 2004 at 21:17 UTC
|
This is the definition given in the C++ Standard:
17.1.18 traits class [defns.traits]
a class that encapsulates a set of types and functions necessary for template classes and template functions
to manipulate objects of types for which they are instantiated. Traits classes defined in clauses 21, 22 and
27 are chararacter traits, which provide the character handling support needed by the string and iostream
classes.-- ISO/IEC 14882:1998(E)
In Standard C++ these are used to package the basic character operations for equality, dictionary ordering, string length, searching, movement and copying. There, they are characterized by the requirements that they present a unified interface and that each character class provides a concrete implementation of that interface. They sound very like the set of conditions you cite.
Under Perl's looser type regimen, generic (template) programming is usually regarded as automatic. An instance class's methods are assumed to provide the needed interface, and it is up to the programmer to make sure they automatically do ;-)
It seems to me that there are two fairly natural implementations available in Perl 5.
- By inheritance from a virtual base class. The interface functions are written there in terms of declared but undefined primitive methods which are private to the derived class, which must implement them somehow. Flattening is achieved by ordinary inheritance. The usual caveats of multiple inheritance apply.
- By Perl's attribute mechanism. That is notationally convenient, but is still a black art for many Perlers (myself included). Perhaps this is less an alternative than an element of #1.
One less-known aspect of C++ traits is that their methods do not throw. Is some no-error convention appropriate for Perl traits?
| [reply] |
Re: Class::Trait to the CPAN?
by hardburn (Abbot) on Feb 16, 2004 at 17:15 UTC
|
The other reason is pretty straightforward: people don't know much about traits and if no one is likely to use the module, I don't see much sense in adding to the terminology glut.
IMHO, people aren't going to learn much about traits unless they can use them in practical examples (at least, I sure won't, though I'm more of a "learn by doing" type of person). I'd like to see a useable version of traits so I can figure out how to best use the concept before Perl6 gets going.
----
: () { :|:& };:
Note: All code is untested, unless otherwise stated
| [reply] [d/l] |
Re: Class::Trait to the CPAN?
by dragonchild (Archbishop) on Feb 16, 2004 at 21:21 UTC
|
Personally, I have an actual production need for this. After I read the paper, I idly thought of implementing Class::Trait myself, (but am waaay too lazy).
I've got a Base class, called Object::Base. I've also got Object::Singleton and Object::ReadOnly. So, now, I want to be able to create a readonly singleton. Ok. Not a problem. Except, I don't want to create a Object::ReadOnlySingleton class. (I anticipate adding 3-4 other orthogonal attributes.)
The problem is depth-first method searching. There's some dovetailing between how O::Singleton and O::ReadOnly do their work. O::Singleton calls a method that O::ReadOnly overrides, and vice-versa. If I inherit from them both, I miss whichever is second in @ISA.
So, in short - yes, I want this. I'll be treating Traits as a neat implementation of Decorator, but it still works. :-)
------
We are the carpenters and bricklayers of the Information Age.
Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.
| [reply] |
|
|
| [reply] |
Re: Class::Trait to the CPAN?
by simonm (Vicar) on Feb 16, 2004 at 21:40 UTC
|
I'd encourage you to upload it to CPAN. The module collection is large and diverse enough that additional dilution or "glut" seems like a misplaced concern.
Particularly in interesting areas like this where there's active ongoing discussion and consideration, I think it's helpful to have a handful of different implementations available that people can put through their real-world paces... And looking at different takes on a similar problem can help to clarify some of the core commonalities and separate out various useful idioms.
In response to the questions in the OP, no, I haven't yet used a traits-based system, but I have spent a bunch of time hacking different types of method aggregation, generation, and composition techniques in Perl, so yes, I would be interested in seeing how or if traits would help me address some of the same concerns.
It would be very helpful to have a more realistic example that both synopsized the Class::Trait interface and also demonstrated how such traits can be useful... I haven't yet been able to visualize a compelling example case, but I'll give it some more thought. | [reply] |
Re: Class::Trait to the CPAN?
by adrianh (Chancellor) on Feb 16, 2004 at 23:42 UTC
|
| [reply] |
Re: Class::Trait to the CPAN?
by halley (Prior) on Feb 17, 2004 at 14:41 UTC
|
I guess I independently invented this idea a few years back, but instead of the word 'trait' I used the word 'quality.' I use the term 'attach' for adding a quality to a given object instance, because I also support removing (via 'detach') qualities at runtime also. My qualities do support state information, which does constrain the object model to support that.
(I'll be digging into the linked PDF and other folks' ideas here to see how I can improve my own model. Thanks!)
My implementations in Java supported runtime attachment and detachment of qualities, but my draft Perl implementation has been a little heavy and inelegant so far.
These are very handy in design of various roleplaying games; you can add the "edible" quality/trait to an object and it now supports the verb 'eat.'
If classes are nouns, qualities/traits are adjectives.
Once you get used to the idea of qualities/traits, your class hierarchy is a lot different: most classes are really just groups of qualities without much to do directly. For example, a typical player-character in a MUD, just after drinking a magical potion, might look like:
class Human
is Mammal
with Bipedal, Sentient, Inventoried, Skilled, Clothed
instance 3453
is Human
with Character, Flying, Enchanted
After the flying-potion enchantment wears off, just remove Enchanted, which in turn can remove Flying.
-- [ e d @ h a l l e y . c c ]
| [reply] [d/l] |
Re: Class::Trait to the CPAN?
by Abigail-II (Bishop) on Feb 16, 2004 at 17:03 UTC
|
Have you ever used traits in any language?
Except for some exercises about interfaces I did during a
Java course, the answer is no.
Would you use a traits implementation in Perl?
Unlikely.
How would you improve the interface above?
No constructive answer. But looking at your code, I'm at a
loss as what is happening. I'm trying to map it on Java's
interface model, but I don't succeed. This maybe though because I remember Java's interface model wrongly.
Abigail
| [reply] |
|
|
My code and explanation don't really stand alone. If the original traits paper is read, it makes more sense. If you're familiar with mixins, then traits can be loosely thought of as mixins that have conflicts explicitly resolved (thus eliminating the ordering problem of mixins). Also, unlike mixins (as I understand them), a trait may require certain methods (expectations) that it does not itself implement. Thus, if you have a polygon class and you want furry polygons (whatever that means), then you might use a "Furry" trait. That trait might expect a paint method, but not implement it directly. Instead, it expects that it will be provided once the traits are flattened into the primary class.
I see now that my description of interfaces was misleading. In Java, of course, an interface is merely a listing of methods that a class must implement. This is checked at compile time. For Class::Trait, I internally add methods to an "interface" and this interface is then validated when the Class::Trait->assemble method is called. Thus, if the "Furry" trait expects a paint method, this is added to the interface. The "interface" that I describe is dynamically generated and not validated until all required traits are examined. When assemble() is called, the methods are flattened and if a paint method is not found, the program dies, thus ensuring that, at the very least, the required methods are present (though there's no way in any language I know of to verify that those methods do what they should, though being able to specify method signatures would help).
| [reply] |
|
|
CHECK {
die unless __PACKAGE__ -> can ("paint");
}
except that the actually check is delayed till run time?
In that case, I'd rather see an interface like:
use Class:Trait qw /paint/;
with no explicite assemble call, but have
the checking done at compile time. That might be useful
if you write a module that are only useful if callbacks
are installed.
Abigail | [reply] [d/l] [select] |
Re: Class::Trait to the CPAN?
by stvn (Monsignor) on Feb 17, 2004 at 21:00 UTC
|
Have you ever used traits in any language?
Nope.
Would you use a traits implementation in Perl?
Depends upon who good it was :)
How would you improve the interface above?
To start with I would not use the Trait::* namespace in the way you are. It is making assumptions that my application does not have another need for that namespace. I see what you are trying to do with it, and I recomend that you make all trait's inherit from a base Trait class instead.
I would also combine the 2 use statements into one, so that:
use Class::Trait 'LifeGuard';
use Class::Trait 'Dog' => { explicit => 'swim' };
would turn into :
use Class::Trait (
'Trait::LifeGuard',
'Trait::Dog' => { explicit => 'swim' }
);
Your Class::Trait::import() code would just need to look-ahead after every trait name it found to see if there is a hash-ref to process. Pretty simple.
If all your trait classes inherit from a Trait base, further compile time checking can be done too. You can basically check that each trait being imported is a valid Trait object and is actually implemented (i.e. - exists in the symbol table).
I would also get rid of all this:
Class::Trait->promise('doggie_treat');
Class::Trait->assemble; # this flattens the traits into this class
sub AUTOLOAD {
# we'll make &doggie_treat, if needed
}
The promise should be auto-checked at compile-time when you load Trait::Dog with the use statement. Basically you get the list of promises for each Trait and then make sure they exist in (at a minimum) the symbol table class importing said Trait. This would allow you to use AUTOLOAD as you did above by just adding sub doggie_treat; somewhere, but I would discourage the use of AUTOLOAD because IMO it weakens the promise.
As traits are added, promises made and expectations listed, I build a composite interface (similar to a Java interface). When Class::Trait->assemble (should I call that 'flatten'?) is called, the interface is validated and conflicts are resolved.
I am not sure what you are actually doing by this description, but IMO the act of "assembling" the code (or "flattening" as the paper refers to it), should be done also at compile time. So that by the time you would reach the CHECK stage of perl compilation cycle all your traits methods should be attached and promises checked (not just made) and in general you should have a trait-object which would incur little if any additional run-time cost.
This means that I think you should alias all your trait methods to be members of the class that is using them (this is how they describe the implementation in paper). Just a simple construct like this would do:
*{"${using_pkg}::$trait_method_name"} = \&{"${trait_pkg}::$trait_metho
+d_name"}
Ideally the fact your class uses traits, would never be known by any other class or module that it might encounter. This may be an idealistic POV, but its something to strive for :).
Taking all these things into account your examples would look like this:
package Person;
use Class::Trait (
'Trait::LifeGuard',
'Trait::Dog' => { explicit => 'swim' }
);
sub doggie_treat {
my ($self) = @_;
return "Snausages,.. yummmm";
}
1;
The new (slightly expanded) traits look like this:
package Trait::LifeGuard;
# make it inherit from some
# kind of base Trait object
use base 'Class::Trait::TraitBase';
sub swim {
my ($self, $target) = @_;
# would probably want to pass on any
# arguments here
return Trait::LifeGuard::_swim($self, $target);
}
# i am fleshing this out a bit to show
# how if $self->swim was actually
# the Trait::Dog implementation it
# would still work
sub save_drowning_swimmer {
my ($self, $swimmer) = @_;
my $shore = $self->station()->location();
$self->swim($swimmer->location());
$self->grab($swimmer);
$self->swim($shore);
}
# i used these two methods, so lets
# define them here anyway
sub grab {}
sub station {}
1;
package Trait::Dog;
# make it inherit from some
# kind of base Trait object
use base 'Class::Trait::TraitBase';
# make this into a variable of some
# kind, kind of like @EXPORT maybe?
my @EXPECTS = ('doggie_treat');
sub swim {
my ($self, $target) = @_;
# since we know that the 'swim' method is now
# a valid method in our $self object, we can call
# 'doggie_treat' on $self with confidence since all
# this will have been checked at compile time
Trait::Dog::_eat($self->doggie_treat());
# swim to $target
# passing on any arguments here
# that might be nessecary ...
Trait::Dog::_swim($self, $target);
}
sub _eat {
# this must be called via a fully qualified name because private
# methods are not flattened into the primary class
}
1;
Probably your best bet as to an example/test would be to try and built the 'Shape' example found in the traits paper itself. They explain it is great detail and you can use that as a bar to hold your implementation against. You may want to pick up a Smalltalk book if you really want to understand what they are doing there too.
-stvn | [reply] [d/l] [select] |
Re: Class::Trait to the CPAN?
by Anonymous Monk on Feb 17, 2004 at 07:59 UTC
|
Ovid, you asked:
* Have you ever used traits in any language?
Have You? On anything other than toy examples? It sure doesn't
sound like it (apologies if that is incorrect). Might it not be a good idea
to try out Traits yourself on at least one real world project before
releasing it where unsuspecting people might assume it was designed and
written by someone with actual working knowledge.
| [reply] |
|
|
That's actually an excellent point. ++. I read the traits paper and saw a lot of potential, so I decided to implement the idea. However, given that I've not found much real-world usage, there's not much prior art for me to go on, other than following the Smalltalk examples (and I don't know Smalltalk well). Hence this post seeking advice.
The package is currently at version 0.02, so it's pretty clear that it's alpha code, but I should add a strong caveats section to the docs -- namely that I've not used traits before in any real project and thus am less sure of the interface or even the best usage.
You wrote that it doesn't sound like I've used traits before. Did you get that impression because of some misunderstanding on my part? If so, I'd love to have it cleared up.
| [reply] |
|
|
You wrote that it doesn't sound like I've used traits before. Did you get that impression because of some misunderstanding on
my part? If so, I'd love to have it cleared up.
I got that impression simply from the lack of any discussion of real world
usage in your posts here and on use.perl. And from the kinds of questions
you were asking.
Don't get me wrong. I do not think it is a bad idea that you are writing
this module, I just think that CPAN'ing it is somewhat premature given
your lack of working knowledge of Traits.
I'd suggest at least trying it out on one of your existing (real) projects
to get a solid feel for how Traits can affect your design (composability
wise). First, using what you know of Traits from the theoretical
perspective, write yourself some 'Use Cases' for how you would like to
apply Traits (that's right, programmer's should write Use Cases for
themselves when designing new libraries, modules, methodologies, etc.
After all, the programmer is the "user/customer" of such technologies).
Then look to reworking an existing project with a non-trivial class/object
hierarchy that might benefit from composing with Traits. Let your design
be informed by theory, but driven by practice.
| [reply] |
Re: Class::Trait to the CPAN?
by stvn (Monsignor) on Feb 18, 2004 at 20:12 UTC
|
I was searching around on the web for more info on Traits (I may be needing to implement them or something like them in C#/.NET/MSILasm in the near future). And I actually found this page which has the original paper, along with a technical "experience report" on implementing the Smalltalk Collection class heirarchy with Traits as well as a paper on a formal model for Traits. I am printing them out right now for the train ride home, but haven't read them yet. Ovid, I thought you would especially be interested in this, and maybe chromatic too (maybe even Larry, Damien & the Perl 6 posse as well (assuming they already don't know about them)).
Update:
Just also found this paper (not for the faint of heart) - A typed calculus of traits (PDF). Which came from this page on Lamda The Ultimate.
-stvn
| [reply] |
|
|
| [reply] |