Anyone for Perl 6 metaprogramming?
My last post about the Metaprogramming: Ruby vs. Javascript blog post stirred a little thought in my head:
How would it look in Perl 6?
Well here goes, the complete example written in Perl 6 which runs on Rakudo, an implementation of the Perl 6 spec on the Parrot VM:
use v6; class Ninja { has Str $.name is rw; } my Ninja $drew .= new( name => 'Drew' ); my Ninja $adam .= new( name => 'Adam' ); ########################################################### # Reopen Ninja class ("is also" does the biz) # and add 'battle_cry' method class Ninja is also { method battle_cry { say $.name ~ ' says zing!!!'; } } $drew.battle_cry; # => Drew says zing!!! $adam.battle_cry; # => Adam says zing!!! ########################################################### # add 'throw_star' method to $drew object by creating # and applying ("does") role to it (Singleton method) role ThrowStar { method throw_star { say "throwing star" } } $drew does ThrowStar; $drew.throw_star; # => throwing a star ########################################################### # call method dynamically: $obj.'method_name' or $obj.$method $drew.'battle_cry'; # => Drew says zing!!! ########################################################### # add "colour" method closing over $colour_name (ie. closure): my $colour_name = 'black'; class Ninja is also { method colour { say "{$.name}'s colour is {$colour_name} " } } $drew.colour; # => Drew's colour is black $adam.colour; # => Adam's colour is black ########################################################### # "defining a method dynamically on an instance that closes # over local scope and accesses the instance’s state" my $sword_symbol = '********'; $drew.^add_method( 'swing', method ( Str $sound_effect ) { say "{$.name}: {$sword_symbol} {$sound_effect}"; }); $drew.swing( 'slash!!' ); # => Drew: ********* slash!!
Of all the examples (in Perl 5 / Moose, Ruby, Javascript & Python) I think this one is the most clean and intuitive. Perl 6 has a good future if it keeps this up!
And it all went surprising smoothly. There were a couple of bumps in my Perl 6 road but these are all connected to the flux between the Perl 6 spec, Rakudo and knowing what info you find on the web is still accurate (or not).
First bump was how to re-open a class. Most Perl 6 documentation implied that classes were always open (unless is final
was used). This no longer seemed to be the case and it gave examples of class A is augmented { ... }
. However this didn’t work in Rakudo but eventually I came across is also
which did.
I like the look of class A is also { ... }
. However Moritz on IRC #perl6 mentioned that the Perl 6 spec had changed so my example would need to be:
augment class Ninja { method battle_cry { say $.name ~ ' says zing!!!'; } }
Rakudo is currently a bit behind Perl 6 spec here but above will replace is also
shortly. The change to augment makes sense because it avoids any confusion with roles in the class declaration.
The second bump was how to create a method dynamically. Now being familiar with Moose I had expected the following to work:
Ninja.meta.add_method( 'swing', method ( Str $sound_effect ) { say "{$.name}: {$sword_symbol} {$sound_effect}"; });
And according to most Perl 6 docs .meta
was the method to access the metaclass (MOP). However meta produced a method not defined error 😦
Trawling the web further I did find that you could do:
Ninja.swing = method { say "Changed to zing" };
But this only allowed you to redefine a method, not create a new one.
Luckily IRC #perl6 came to the rescue again! jnthn & Moritz pointed me to $object.^add_method( ... )
and the docs on the ng branch of Rakudo on Github.
Perl6 does have some beautiful syntax. I love the $drew does Throwstar
line. Soon Rakudo will be able to do:
$drew does role { method throw_star { say "throwing star" } };
So this, augment and probably lots more will be part of the ng branch which will be merged with the Rakudo master in the not too distant future.
/I3az/
Note to everyone: An up-to-date version of this perl6 example while Perl 6 / Rakudo develops can be found at this gist: http://gist.github.com/276591
/I3az/
Actually, the main reason for abandoning “is also” is that it turned the name lookup into a semantic pretzel retroactively. With the “augment” declarator out front, we already know when we’re parsing the class name that we will be looking up an existing name, not adding a new name. This simplifies the internal logic since we don’t have to change anything after the fact; we just do the correct lookup when we see the name. And this is important because, unlike in Perl 5, names are always introduced immediately, and can be referred to later in the same expression, so we need to poke them into the symbol table as soon as possible, without having to worry about having to “unpoke” them later. We can also issue name collision errors immediately, instead of having to remember to check later.
Hi Larry,
Many thanks for leaving such a wonderfully insightful comment.
I suppose this is an example of a language being pulled between something beautifully linguistic as
class Ninja is also {}
compared to something more parser/compiler efficient asaugment class Ninja {}
.Keep stirring that pot because Perl 6 seems to be fermenting into something really lovely.
/I3az/
Re: the add_method thing, the more mooselike way would be $object.HOW.add_method($object, ‘methname’, method { … }), with HOW being the Perl6 name for the meta-thing. The ^-syntax is a convenience shortcut for this that also passes the object itself along for metaclass methods that want it.
Hi Andrew,
Funny enough I did know about HOW (and WHAT) and had also seen that ^ was use as a shortcut but they had all completely escaped my memory when I was looking and kept hitting docs with .meta method… probably because I was searching for “meta” 😦
Clearly the old Perl6 docs on the internet could be an issue going forward (in fact when I was on IRC that day mentioned in above post there was someone else hitting exactly same prob).
/I3az/
Hi draegtun
a question :
and now ?
the
class Ninja is also {…}
not support anymore??
Hi ques,
“is also” is now deprecated for reasons described by Larry in the comments above.
See my newer post which nows uses “augment class Ninja {}” instead of “class Ninja is also{}” which works in Rakudo Star. https://transfixedbutnotdead.com/2010/10/31/perl6-metaprogramming-update/
/I3az/