As it turns outs, implementing traits at runtime isn't too hard. It's the syntax which is killing me. Let's say that I have a new "Dog" object and I want my dog to do tricks. In Perl 6:
$fido does Tricks;
Class::Trait was not originally designed to support runtime trait composition so the does method either returns a list of traits a class can do or returns a boolean value indicating that a particular trait passed to it is avaiable. Thus, we use the apply method for adding traits. However, this raises a number of thorny issues. First, according to Synopsis 12, roles can be applied at runtime to both classes and instances and this will create a new anonymous class. For instances, this is easy. What does this mean for classes, though? I'm not sure. Bummer.
Applying them at runtime for instances is easier. I need to rebless an object into the appropriate anonymous class and flatten the trait methods into that class. However, I don't have an appropriate syntax. I could do this:
require Tricks; my $fido = Dog->new; Tricks->apply($fido);
Unfortunately, Synopsis 12 makes it clear that we should be able to apply several traits to fido at once, flattening them into a new anonymous class. The only way I can see to make that work is a variant of this:
Class::Trait->apply($fido, @list_of_traits);
That seems like a really ugly syntax. However, more than this, how would I then apply traits to a class? I could do this:
Class::Trait->apply(__PACKAGE__, @list_of_traits);
That seems like an even uglier syntax but I can't think of a better way. For the time being, runtime trait application seems best suited for instances, so I'll focus on that.
Applying several traits to an instance should not do anything different than applying one. So for instance this (in pseudo Perl 6):
Would become this (in Perl 5 Traits):$fido does Tricks does Treats;
Basically apply should return the reblessed instance, and you just keep chaining them.Treats->apply(Tricks->apply($fido));
Now, this may not be the most efficient approach, but I am not really sure that would matter since runtime application of multiple traits will probably be a rare occurance. The better way to accomplish this (IMO of course) is to compose a third Trait out of the multiple traits you have applied, then apply that third trait to your instance.
As for the application of traits to classes at runtime, I think it is actually quite simple (although it might require fiddling with Class::Trait internals to make this work right). You basically just need to add in the trait as you would at compile time. This makes (some) sense if you think of classes as being singleton instances of class Class.
Re:
Aristotle on 2005-11-26T14:32:25
The better way to accomplish this (IMO of course) is to compose a third Trait out of the multiple traits you have applied, then apply that third trait to your instance.
I think that’s the key to a bearable syntax: have a
Class::Traits::compose
, returning an anonymous trait which is a composition of the given traits, on which you then simply invokeapply
:
Class::Traits->compose( "Tricks", "Treats" )->apply( $fido );