Skip to content

Anyone for Perl 6 metaprogramming?

January 14, 2010

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/

10 Comments leave one →
  1. January 15, 2010 9:38 am

    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/

  2. Larry Wall permalink
    January 15, 2010 5:36 pm

    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.

    • January 24, 2010 5:00 pm

      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 as augment class Ninja {}.

      Keep stirring that pot because Perl 6 seems to be fermenting into something really lovely.

      /I3az/

  3. Andrew permalink
    February 25, 2010 6:52 am

    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.

    • March 4, 2010 7:11 pm

      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/

  4. October 29, 2011 7:35 am

    Hi draegtun

    a question :

    and now ?
    the
    class Ninja is also {…}

    not support anymore??

Trackbacks

  1. uberVU - social comments
  2. Been reddited « transfixed but not dead!
  3. Perl6 metaprogramming update « transfixed but not dead!

Leave a comment