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!

Day 13 – String Interpolation and the Zen Slice

So you think you know all about string interpolation in Perl 6?

Well, especially coming from Perl 5, you may find some things that do not work exactly as you’re used to. The simple examples all work, as it seems:

my $a = 42;
say 'value = $a'; # value = $a
say "value = $a"; # value = 42

my @a = ^10;
say 'value = @a'; # value = @a
say "value = @a"; # value = @a HUH??

In earlier versions of Perl 5 (or was it Perl 4?), this gave the same result. At one point, it was decided that arrays would be interpolated in double quoted strings as well. However, this caused quite a few problems with double quoted texts with email addresses in them: you would need to escape each @ (if you were using strict, which you of course should have). And if you forgot, and there was an array that just happened to have the same name as the user part of an email address, you would be in for a surprise. Or if you didn’t use strict, you would suddenly have the text without the email address. But then again, you got what you asked for.

So how can we make this work in Perl 6?

Introducing the Zen slice

The Zen slice on an object, returns the object. It’s like you specify nothing, and get everything. So what does that look like?

my @a = ^10;
say "value = @a[]"; # value = 0 1 2 3 4 5 6 7 8 9

You will have to make sure that you use the right indexers for the type of variable that you’re interpolating.

my %h = a => 42, b => 666;
say "value = %h{}"; # value = a 42 b 666

Note that the Zen slice on a hash returns both keys and values, whereas the Zen slice on an array only returns the values. This seems inconsistent, until you realize that you can think of a hash as a list of Pairs.

The Zen slice only really exists at compile time. So you will not get everything if your slice specification is an empty list at runtime:

my @a;
my %h = a => 42, b => 666;
# a slice, but not a Zen slice:
say "value = %h{@a}"; # value =

So the only way you can specify a Zen slice, is if there is nothing (but whitespace) between the slice delimiters.

The Whatever slice

The * ( Whatever ) slice is different. The Whatever will just fill in all keys that exist in the object, and thus only return the values of a hash.

my %h = a => 42, b => 666;
say "value = %h{*}"; # value = 42 666

For arrays, there isn’t really any difference at the moment (although that may change in the future when multi-dimensional arrays are fleshed out more).

Interpolating results from subs and methods

In double quoted strings, you can also interpolate subroutine calls, as long as they start with an ‘&‘ and have a set of parentheses (even if you don’t want to pass any arguments):

sub a { 42 }
say "value = &a()"; # value = 42

But it doesn’t stop at calling subs: you can also call a method on a variable as long as they have parentheses at the end:

my %h = a => 42, b => 666;
say "value = %h.keys()"; # value = b a

And it doesn’t stay limited to a single method call: you can have as many as you want, provided the last one has parentheses:

my %h = a => 42, b => 666;
say "value = %h.perl.EVAL.perl.EVAL.perl()"; # value = ("b" => 666, "a" => 42).hash

Interpolating expressions

If you want to interpolate an expression in a double quoted string, you can also do that by providing an executable block inside the string:

say "6 * 7 = { 6 * 7 }"; # 6 * 7 = 42

The result of the execution of the block, is what will be interpolated in the string. Well, what really is interpolated in a string, is the result of calling the .Str method on the value. This is different from just saying a value, in which case the .gist method is called. Suppose we have our own class with its own .gist and .Str methods:

class A {
    method Str { "foo" }
    method gist { "bar" }
}
say "value = { A }"; # value = foo
say "value = ", A;   # value = bar

Conclusion

String interpolation in Perl 6 is very powerful. As you can see, the Zen slice makes it easy to interpolate whole arrays and hashes in a string.

In this post I have only scratched the surface of string interpolation. Please check out Quoting on Steroids in a few days for more about quoting constructs.