🔬71. Implementing Int.sleep() in Perl 6

🔬71. Implementing Int.sleep() in Raku

N. B. Perl 6 has been renamed to Raku. Click to read more.


Hello! Yesterday, I was giving my Perl 6 Intro course at the German Perl Workshop in Gummersbash. It was a great pleasure to prepare and run this one-day course, and, while it was difficult to cover everything, we touched all main aspects of the Perl 6 language: from variables to regexes and parallel computing. Of course, it was only a top-level overview, and there was not enough time to make all the exercises. You can do them at home, here’s the Perl 6 Intro – Exercises PDF file.

Among the rest, we tried to implement the sleep method for integers. The rationale behind that is that it is possible to say:

> 10.rand
9.9456903794802

But not:

> 10.sleep
No such method 'sleep' for invocant of type 'Int'
  in block <unit> at <unknown file> line 1

OK, so let’s first implement the simplest form of sleep for Ints only. Go to src/core/Int.pm6 and add the following:

my class Int does Real {

    method sleep() {
        nqp::sleep($!value);
    }

Here’s a photo from the screen:

29695497_10156162162038326_7927919948344098147_n

There is no declaration of the $!value attribute in this file, but we know that it can be found somewhere in Perl6/Metamodel/BOOTSTRAP.nqp:

# class Int is Cool {
# has bigint $!value is box_target;
Int.HOW.add_parent(Int, Cool);
Int.HOW.add_attribute(Int,
    BOOTSTRAPATTR.new(:name<$!value>, :type(bigint), 
                      :box_target(1), :package(Int)));
Int.HOW.set_boolification_mode(Int, 6);
Int.HOW.publish_boolification_spec(Int);
Int.HOW.compose_repr(Int);

Compile and run. The desired code works now:

> 3.sleep
# sleeping 3 seconds
>

What can be changed here? The first idea is to allow non-integer numbers as the delay duration. As Int does the Real role, just move the method to src/core/Real.pm and get the value using the Num method instead of reading $!value directly (there is no such attribute in the Real role):

my role Real does Numeric {

    method sleep() { 
        nqp::sleep(self.Num);
    }

Now it also works with rationals and floating-point numbers:

> 2.sleep
2

> 3.14.sleep
3.14

> pi.sleep
3.14159265358979

Before wrapping it up, let us take a look at the body of the sleep subroutine. It is defined in src/core/Date.pm6:

proto sub sleep(|) {*}
multi sub sleep(--> Nil) { sleep(*) }
multi sub sleep($seconds --> Nil) {
    # 1e9 seconds is a large enough value that still makes VMs sleep
    # larger values cause nqp::sleep() to exit immediatelly (esp. on 32-bit)
    if nqp::istype($seconds,Whatever) || $seconds == Inf {
        nqp::sleep(1e9) while True;
    }
    elsif $seconds > 1e9 {
        nqp::sleep($_) for gather {
            1e9.take xx ($seconds / 1e9);
            take $seconds - 1e9 * ($seconds / 1e9).Int;
        }
    }
    elsif $seconds > 0e0 {
        nqp::sleep($seconds.Num);
    }
}

The code is very clear and does not need any comments.

And maybe just to see why our modified Rakudo printed the time after sleep in the tests above, let’s refer to the documentation of NQP to see that its sleep function’s return value is the number of seconds:

## sleep
* `sleep(num $seconds --> num)`

Sleep for the given number of seconds (no guarantee is made
how exact the time sleeping is spent.)
Returns the passed in number.

 

One thought on “🔬71. Implementing Int.sleep() in Perl 6”

Leave a Reply

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

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code