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