On Sat, 24 Apr 2004, John Siracusa wrote:
> Based on the "default accessors and encapsulation" thread, it seems like a
> Perl 6 equivalent of Class::MethodMaker will be still be useful in our (or
> at least "my") Brave New World. I've been pondering the best way to create
> such a beast in Perl 6.
Yes, I agree. As you point out below, Class::Makemethods does lots of
crazy stuff. Much of that (like pre- and post-hooks) will be easier to
write in P6, but there is still lots of stuff that won't be in the core.
>
> The most common two Perl 5 techniques are:
>
> 1. Use a string eval: build up a big string that looks the code for the
> method that I would have typed myself if I wasn't so lazy, then eval the
> string and assign the resulting code ref to the appropriate typeglob.
> Example:
>
> $attr = 'baz';
> *{"Foo::$attr"} = eval qq(sub { \$_[0]->{'$attr'} });
> This technique seems to have the best runtime performance in Perl 5 (by a
> small margin), but it's also much more expensive (not to mention tedious and
> persnickety) to create the method in the first place. For whatever reason,
> it's always just struck me as "wrong" (sort of like source filtering where
> code is just seen as a giant string--something that Perl 6 blessedly saves
> us from :)
>
> 2. Use a closure: build a method by assigning what would normally be
> constant values to a set of variables, then capturing their state in a
> closure. Example:
>
> $attr = 'baz';
> *{"Foo::$attr"} = sub { $_[0]->{$attr} };
Symbol tables and typeglobs and such belong to A10... and the * has been
stolen... so I'll just speculate in pseudocode.
Blocks-are-subroutines makes life easier, and in pseudocode that can be
just:
*{"Foo::name1"} = -> $a { $a->{name1} };
OR:
for @names -> $name {
my $private_scalar = $name;
*{"Foo::$name"} = -> $a {$a->{$private_scalar}};
}
>
> Making the method this way has always seemed "cleaner" to me, but it bothers
> me that $attr a full blown variable that has to be read from every time the
> method is run.
> Really, it should be a constant, which is probably why the
> string eval version has a speed edge at runtime.
That is something the compiler may be able to deal with. I don't know much
about compilers, but here is something from the camel book, 3rd ed, page
229 about inlining functions:
BEGIN {
my $prod = 1;
for (1..10) { $prod *= $_; }
sub NFACT () { $prod }
}
Here, NFACT is inlined because the compiler sees that $prod can never
change. So maybe the $private_scalar above can also be inlined. (There is
a new $private_scalar each time through the loop because of the my, and
$name is also implicitly my). For that code I did not need to introduce
$private_scalar, but I put it there to stand for more complex calculations
if you need there.
Maybe there should be a way to give hints to the compiler that inlining
maybe possible.
> The two Perl 5 techniques still seem like they will work (albeit with
> different syntax), but I'm sure there are better ways...perhaps something
> involving macros? The ideal solution has the strengths of both Perl 5
> techniques, but none of their weaknesses. Creation should be fast and
> clean, and there should be no wasted overhead when calling the generated
> methods. It should be just as if I wrote the methods myself.
>
> I haven't retained enough Perl 6 syntax to have any idea what this would
> look like, so I'm looking for suggestions. Which Perl 6 features are best
> suited to creating a good Perl 6 MethodMaker? Anyone want to post some
> simple examples?
Wicked Traits come to mind. They seem to be all about warping class
behaviors...
role methodmaker{
method trait_auxillary:install( : Class $container, $method_semantics,
[EMAIL PROTECTED]){
given $method_semantics {
when "semantics1" {
for @names -> $name {
...
my $method = -> $arg {...};
$container.install($name, $method);
}
for @names -> $name {
my $method = $container.get_method($name);
$method.i_wont_change___inline_variables_if_possible;
}
}
}
}
}
# And in our class:
class Dog{
install "some_semantics", <<bark howl whine>>;
}
Or perhaps you want to put the install in a BEGIN{} if you want to have
the body of the class see these (especially their signatures).
class Dog{
BEGIN{ install "some_semantics", <<bark howl whine>> }
}
> -John
--abhijit