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/