I've been following the discussion of "traits" on the Perl 6 lists and here in the monastery with a fair deal of interest, but also a vague feeling of uncertainty -- I didn't feel like I had a firm grip on exactly what the core idea was.

Then earlier today, a very Perlish analogue struck me: on some level, "trait classes" are "method exporters".

For example, here's a hypothetical trivial trait:

package Trait::SelfDumping; require Carp; require Exporter; require Data::Dumper; @EXPORT = qw( dump_self ); sub import { goto &Exporter::import } sub dump_self { my $self = shift; Carp::carp("Self is " . Data::Dumper::Dumper($self) ) } 1;

Now I can add the trait of being self-dumpable to any other class I build:

package My::FooBar; use Trait::SelfDumping ':default'; sub new { ... package main; my $foo = My::FooBar->new(); $foo->dump_self();

A proper traits mechanism should also provide for aliasing method names and for double-checking that other methods that yours depends on have been provided, but those are merely elaborations.

I can imagine an Exporter::Traits module that functioned like Exporter and incorporated the aliasing and missing-methods checks. (Exporter::Renaming already touches on the aliasing aspect. Checking for missing methods is harder, because they may be being provided by another trait, but I presume it can be done.)

Parallel to how Class::Trait works, packages that were going to implement a trait would "use Exporter::Traits" and declare various package variables and subroutines. However, on the caller's side, the interface would follow Exporter, so you could "use" the trait package directly, rather than having to know that it was a trait-driven package.

Have I overlooked something vital in reducing the traits model to a gussied-up Exporter?

And am I right in thinking that viewing this as an Exporter trick makes the concept more accessible to Perl geeks?

Replies are listed 'Best First'.
Re: Roles as Method Exporters
by chromatic (Archbishop) on Apr 10, 2004 at 17:14 UTC

    (I think you mean Roles, not Traits -- in Perl 6 terms, there's a big difference.)

    You're right in that viewing a role as a gussied up export works, but it works in the same way that you can write OO code in C. Sure, you have to maintain your list of methods appropriately and pass around the invocant manually, but you can get some of the benefit with a bit of clever work.

    What you're missing is that the roles that apply to a class actually affect the type system. That is, you can write a method which expects an argument that does the Stringify role -- because you're going to print it. The callers of that method don't have to coerce their arguments into new, temporary String objects because you didn't require that that argument be a string, just that it does something sensible when you treat it as a string.

    Of course, you do get the benefits of code-reuse without having to maintain a fragile and usually precarious class hierarchy with the plain-Exporter approach.

      I think you mean Roles, not Traits -- in Perl 6 terms, there's a big difference.

      What I meant was "traits as in the software engineering sense" (cf. Composable Units of Behavior"), not in the concrete sense of the Perl 6 language design.

      (Ovid pointed out the vocabulary miss-match a few weeks ago: Roles, incidentally, are essentially the same thing as "traits" in the paper above, but the word "trait" (compile time properties) is already used in Perl 6.)

Re: Traits as Method Exporters
by stvn (Monsignor) on Apr 11, 2004 at 04:59 UTC

    chromatic is right, in Perl 6 land, they are Roles, and that is not the same as traits (as described in the traits papers and as implemented in Class::Trait). Roles is an expansion/evolution on traits, and from that I know of them so far, a much more perlish way of doing them. chromatic wrote an implementation with Class::Roles, but I believe he said he has put further development on hold till the Perl 6 crew figures out more of the details. I wrote Class::Trait actually, Ovid started it but got busy at work (damn that day job stuff), and I ran with it.

    Now, as for traits model being a gussied up Exporter, I would have to disagree. The exporting of subroutines into the implementing package's namespace is only the end result of the traits. Much of the reason why traits make sense (which I think many people overlook, I know I did at first) is because they are strongly grounded in formal theory. The combination of traits (summation), and the use of aliasing, exclusion along with method requirements all create a nice solid little box that traits can live and work in. But even still the exportation of subroutines is only part of what traits need to do, checking for required methods, and handling for method conflicts are vitally important to it all working right.

    If you are interesting in seeing more of the process by which traits are composed I would suggest using the debug setting in Class::Trait. About half the code in that module is debug/trace statements actually. The easiest way to do that is to download the distribution, add the following code to the top of the file t/30trait_composition.t:

    use Class::Trait 'debug';
    then run the file with perl (not through Test::Harness). You will see a about 210 lines of output printed by Class::Trait (its actually about 420 lines in total with the Data::Dumper output) which details the entire trait composition process.

    I would also recommend reading the paper "Traits - A Formal Model" linked to on the traits page, it provides an insight into the theory behind how traits get composed and how they interact with classes. I have included a small exerpt/bastardization/summarization of it below, that I was going to include in the Class::Trait documentation, but never got around to. My apologies to the original authors.


    -stvn