Day 14 – A Perl 6 Timer: Ready, Set, Go!

Christmas is coming!

With Perl 6 promising to be ready for production next year – now is the time to stop lurking and start learning. It’s exciting! But what to try first?

OK – so I’ve downloaded Rakudo and tinkered with the REPL. Time to try translating a simple module that I often use in Perl 5 projects. It’s a simple timer that records events and renders a timed report:

my $timer = Timer->new;
$timer->record('first event');
$timer->record('second event');
say $timer->report;

Here’s the output:

[0.000] first event
[0.007] second event

It helps trace code and identify bottlenecks. Here’s a Perl 5.20 version:


package Timer;

use Time::HiRes qw(gettimeofday tv_interval);
use Mojo::Base -base;
use experimental 'signatures', 'postderef';

has '_event_log'  => sub { [] };
has '_start_time' => sub { [ gettimeofday ] };

sub record ($self, $event_description) {
    # record the event and timestamp
    my $event = {
        timestamp   => sprintf("%.3f", tv_interval($self->_start_time)),
        description => $event_description,
    };
    push($self->_event_log->@*, $event);
}

sub report ($self) {
    # render a full report
    my $report = '';
    foreach my $event ($self->_event_log->@*) {
         $report .= '[' . $event->{timestamp} . '] ' . $event->{description} . "\n";
    }
    return $report;
}
1;

Now how would this look in Perl 6?

Perl 6 does full flavour, duck quacking OO so we can replace Perl 5’s package and declare a Timer class:


class Timer {
    ...
}

No need for that spurious 1; at the end of the file any more either. We need to store an initial start time and a list of event records. In Perl 6, object attributes are defined with the help of ‘twigils’ – that is, secondary sigils …


class Timer {
    has $!start-time;  # private scalar storing the starting time
    has @!event-log;   # private list of events 
                       # dashes-in-identifiers save keystrokes too
}

The exclamation mark here in $!start-time is a secondary sigil and declares the attribute as private. The @!event-log list is also marked as private.

You can imagine the ! character is like a portcullis dropping down at the entrance of a castle – blocking external access to the attribute behind. The . twigil is used for public attributes (e.g., $.this-attribute-is-public-no-portcullis-here).

The twigil tells you at first glance what an attribute’s contract is with the class. No more context-switching and scrolling back to the top of the file to find out. Thanks Larry for saving some brain-space!

So the attributes are defined, what about the methods, record() and report()?

The record() method starts the internal timer the first time it’s called so it needs an equivalent to Perl 5’s Time::HiRes. A quick Google search brings up an example on RosettaCode.org. Like the ancient Rosetta Stone this site is useful for translating between languages and is a hangout for early adopting polyglot programmers. Perl 5 and Perl 6 are well represented there so it’s a great place for converting Perl 5-isms into Perl 6. It seems now is the new gettimeofday!

I had a little chat with the Perl6 REPL about this:


> now # returns an Instant object
Instant:1418191166.678494
> now.HOW # access the Higher Order Workings
Perl6::Metamodel::ClassHOW.new()
> now.^methods # use ^ to access the HOW
new from-posix to-posix Bridge Num Int narrow Rat abs sign conj sqrt rand sin asin cos acos tan atan atan2 sec asec cosec acosec cotan acotan sinh asinh cosh acosh tanh atanh sech asech cosech acosech cotanh acotanh floor ceiling round unpolar cis Complex log exp truncate isNaN base Real log10 roots succ pred sleep Str perl Numeric ACCEPTS Bool gist DUMP <anon>
> now.perl # Data::Dumper in Perl 6
Instant.new(x => <1127461994944/795>)
> now.gist # Human readable explanation
Instant:1418191194.695649
> my $time = now
Instant:1418191201.250955
> now - $time # can we get the difference in time?
7.6888786

So this will do the trick! The record() method works like a stopwatch snapshotting the difference since the start:


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, # take the difference
        description => $event_description
    });
}

The report() method pretty prints the @!event-log:


method report {
    my @report_lines;
    for @!event-log -> %event {
         @report_lines.push("[%event<timestamp>.fmt("%.3f")] %event<description>");
    }
    return @report_lines.join("\n");
}

Metaphorically speaking the for loop unrolls the @!event-log carpet (@) shooting values (->) into individual hash entries (%event)!

Sigils are invariant in Perl 6 so a %hash means %hash and the sigil doesn’t change while accessing a hash value: %hash<key>. Interpolation into a string just works.The full Perl 6 version looks like:


class Timer {
    has $!start-time;
    has @!event-log;

    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");
    }
}

I’m happy with that!

Now … what can I learn next?

How about converting the class into a Perl 6 role instead?

There’s a learning challenge for you too. Can you turn it into a Perl 6 role?

Let me start the timer … Ready, Set, Go!

3 thoughts on “Day 14 – A Perl 6 Timer: Ready, Set, Go!

  1. > duck quacking OO

    In case anyone misunderstands, while Perl 6 can of course do duck typing, it’s formal typing system is nominal, rather than duck or structural.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s