The Pearls of Raku, Issue 11: wrapping things

In this issue, we’ll talk about the built-in wrap routine and its possible applications: wrapping a function, measuring execution time, and logging.

In this issue, we’ll talk about the built-in wrap routine and its possible applications.

Example 1. wrap

The wrap routine — as you may guess from its name — wraps another function. Let us go straight to the example.

sub greet($name) {
    say "Hello, $name!";
}

&greet.wrap({
    callwith($^arg.uc)
});

greet('Venusians'); # Hello, VENUSIANS!

First, we’ve got a regular function that takes a string and prints the message with it. In the end, the function is called as usual.

In-between, we see that the function is wrapped into another function, which is actually called when you call greet instead of it. In this example, the wrapping function calls the original function with the modified argument: callwith($^arg.uc). Here, $^arg is a placeholder variable that the wrapper gets when the original function is called in the last line of the program.

Example 2. unwrap

Let us extend the previous program and remove the wrapper after some time. To do that, you need to save the wrapper object in a variable, which you can later pass to the unwrap routine.

sub greet($name) {
    say "Hello, $name!";
}

my $x = &greet.wrap({
    callwith($^arg.uc)
});

greet('Venusians'); # Hello, VENUSIANS!

&greet.unwrap($x);

greet('Venusians'); # Hello, Venusians!

Example 3. Execution time

One of the useful examples of wrappers can be measuring time of execution of a slow function. Here is how you can do it:

sub slow {
    sleep(1);
}

sub timer {
    my $t0 = now;
    callsame;
    say now - $t0;
}

&slow.wrap(&timer);

slow();

This time, the wrapper uses callsame to call the original function with the same arguments. In this example, I am using a bit different syntax of calling the wrap method. This option is more suitable for bigger wrapping functions.

Example 4. Logging

Another example — a logger that you can place around a function that visits the sites, for example.

use HTTP::UserAgent;

sub browse($url) {
    my $ua = HTTP::UserAgent.new;
    my $response = $ua.get($url);
    say $response.status-line;
}

&browse.wrap(-> $url {
    note "{DateTime.now} $url";
    callsame;
});

browse('https://google.com/');
browse('https://raku.org/');

Again, just for the sake of diversity, I used a pointy block here. You do not need to change the browser function to start logging. Just create a wrapper which does the job:

$ raku wrap-log.raku > /dev/null
2020-09-17T23:04:32.383453+02:00 https://google.com/
2020-09-17T23:04:32.807365+02:00 https://raku.org/

And that’s it for now!

* * *

Find the code of this issue on GitHub and browse for more Raku Pearls.

Leave a Reply

Your email address will not be published. Required fields are marked *