On Day 14 of the Advent calendar I stopped lurking and started learning Perl 6 by converting a Perl 5 Timer object into its Perl 6 equivalent. Day 14 ends with a ‘challenge’ to convert the Perl 6 Timer class into a role.
Perl 6 roles encapsulate functionality for ready reuse by classes and objects.
The Timer
class is a good candidate for ‘role-ification’ – it’s simple and is potentially useful to all sorts of objects.
Santa wants to go Lean this year, don’t worry he’s still rotund – he just wants to apply Lean principles to the process of present giving. The Timer role will help identify bottlenecks so Santa Inc. can improve.
To turn the Timer
class into a role we just swap class
for role
(i.e., s/class/role/
):
role Timer { has $!start-time; # private scalar storing the starting time has @!event-log; # private list of events # dashes-in-identifiers save keystrokes too method record ($event_description) { # initialise the start-time if this is the first time through $!start-time //= now; @!event-log.push({ timestamp => now - $!start-time, description => $event_description }); } method report { my @report_lines; for @!event-log -> %event { @report_lines.push("[%event<timestamp>.fmt("%.3f")] %event<description>"); } return @report_lines.join("\n"); } }
So that’s the role declared. Now I need a class or object to compose it into – something that does
the role. How about a Timer
for Santa’s Helpers?
use Timer; class SantasHelper does Timer { has @!presents = qw<train lego doll> ; method wrap-present($child's_name) { return @!presents.pick ~ " for " ~ $child's_name; } }
A quick check in the REPL shows:
> use SantasHelper; > my $bruce = SantasHelper.new; # this helper has an Australian accent > $bruce.^methods record report wrap-present
Pointing to the Higher Order Workings (HOW) of the $bruce
object via .^
shows the Timer
role’s methods (record
, report
) are now combined with the methods for SantasHelper
(wrap-present
). Let’s start wrapping presents:
use SantasHelper; my $helper = SantasHelper.new; my @children = qw<Hugo Honey Willow>; # using {@children} to interpolate the list $helper.record("started wrapping presents for {@children}"); for @children -> $child { my $present = $helper.wrap-present($child); $helper.record("wrapped $present"); } say $helper.report;
The report shows:
[0.006] started wrapping presents for Hugo Honey Willow [0.013] wrapped train for Hugo [0.016] wrapped doll for Honey [0.020] wrapped doll for Willow
Nice.
But now there’s a problem! Santa’s Lean approach has exposed a bottleneck. To wrap all the presents in time Santa will need to roll up his sleeves and start wrapping too. We need to refactor the wrap-present
method into its own role so Santa
can wrap-presents
too:
role WrappingPresents { has @!presents = qw<doll train lego> ; method wrap-present($child's_name) { return @!presents.pick ~ " for " ~ $child's_name; } }
It turns out we can dynamically add a role to an existing object with does:
> use Santa > my $santa = Santa.new Santa.new() > use WrappingPresents > $santa does WrappingPresents Santa+{WrappingPresents}.new(name => Any) > $santa.wrap-present('Emma') lego for Emma > $santa.wrap-present('Claire') lego for Claire
Santa Inc is now back on track.
It’s been fun playing with Perl 6 roles. Coding in Perl 6 feels malleable and expressive – and I’ve only just started!
jnthn++ has more details about parameterised roles and what happens when you compose multiple roles with the same method signature.
For now I’ll leave the last word to Perl 6’s Santa:
> $santa.greeting(); > Happy Christmas!
Did you test this … ?
The for statment in method report
for @!event-log -> %event {
@report_lines.push(“[%event.fmt(“%.3f”)] %event”);
should be
for @!event-log -> %event {
@report_lines.push(“[%event.fmt(“%.3f”)] %event”);
The version I am running …
This is perl6 version 2014.09 built on MoarVM version 2014.09
2014.09 is ancient – don’t expect too many newer Perl 6 examples to work with that.
You’re right about the angle brackets though.
PS: For now, probably the best way to try an up-to-date Perl 6, is rakudobrew: https://github.com/tadzik/rakudobrew.
Just noticed …. your code is probably correct … just that angle brackets are stripped from the comments … and probably the post as well.