Day 24 – Solving a Rubik’s Cube

Intro

I have a speed cube on my wish list for Christmas, and I'm really excited about it. :) I wanted to share that enthusiasm with some Perl 6 code.

I graduated from high school in '89, so I'm just the right age to have had a Rubik's cube through my formative teen years. I remember trying to show off on the bus and getting my time down to just under a minute. I got a booklet from a local toy store back in the 80s that showed an algorithm on how to solve the cube, which I memorized. I don't have the booklet anymore. I've kept at it over the years, but never at a competitive level.

In the past few months, YouTube has suggested a few cube videos to me based on my interest in the standupmaths channel; seeing the world record come in under 5 seconds makes my old time of a minute seem ridiculously slow.

Everyone I've spoken to who can solve the cube has been using a different algorithm than I learned, and the one discussed on standupmaths is yet a different one. The advanced version of this one seems to be commonly used by those who are regularly setting world records, though.

Picking up this algorithm was not too hard; I found several videos, especially one describing how to solve the last layer. After doing this for a few days, I transcribed the steps to a few notes showing the list of steps, and the crucial parts for each step: desired orientation, followed by the individual turns for that step. I was then able to refer to a single page of my notebook instead of a 30-minute video, and after a few more days, had memorized the steps: being able to go from the notation to just doing the moves is a big speed up.

After a week, I was able to solve it reliably using the new method in under two minutes; a step back, but not bad for a week's effort in my off hours. Since then (a few weeks now), I've gotten down to under 1:20 pretty consistently. Again, this is the beginner method, without any advanced techniques, and I'm at the point where I can do the individual algorithm steps without looking at the cube. (I still have a long way to go to be competitive though.)

Notation

A quick note about the notation for moves – given that you're holding the cube with a side on the top, and one side facing you, the relative sides are:

L (Left) R (Right) U (Up) D (Down) F (Front) B (Back)

If you see a lone letter in the steps, like B, that means to turn that face clockwise (relative to the center of the cube, not you). If you add a ʼ to the letter, that means counter clockwise, so would have the top piece coming down, while a R would have the bottom piece coming up.

Additionally, you might have to turn a slice twice, which is written as U2; (Doesn't matter if it's clockwise or not, since it's 180º from the starting point.)

Algorithm

The beginner's algorithm I'm working with has the following basic steps:

1. White cross 2. White corners 3. Second layer 4. Yellow cross 5. Yellow edges 6. Yellow corners 7. Orient yellow corners

If you're curious as to what the individual steps are in each, you'll be able to dig through the Rubik's wiki or the YouTube video linked above. More advanced versions of this algorithm (CFOP by Jessica Fridrich) allow you to combine steps, have specific "shortcuts" to deal with certain cube states, or solve any color as the first side, not just white.

Designing a Module

As I began working on the module, I knew I wanted to get to a point where I could show the required positions for each step in a way that was natural to someone familiar with the algorithm, and to have the individual steps also be natural, something like:

 F.R.U.Rʼ.Uʼ.Fʼ 

I also wanted to be able to dump the existing state of the cube; For now as text, but eventually being able to tie it into a visual representation as well,

We need to be able to tell if the cube is solved; We need to be able to inspect pieces relative to the current orientation, and be able to change our orientation.

Since I was going to start with the ability to render the state of the cube, and then quickly add the ability to turn sides, I picked an internal structure that made that fairly easy.

The Code

The latest version of the module is available on github. The code presented here is from the initial version.

Perl 6 lets you create Enumerations so you can use actual words in your code instead of lookup values, so let's start with some we'll need:

enum Side «:Up('U') :Down('D') :Front('F') :Back('B') :Left('L') :Right('R')»;
enum Colors «:Red('R') :Green('G') :Blue('B') :Yellow('Y') :White('W') :Orange('O')»;

With this syntax, we can use Up directly in our code, and its associated value is U.

We want a class so we can store attributes and have methods, so our class definition has:

class Cube::Three {
has %!Sides;
...
submethod BUILD() {
%!Sides{Up} = [White xx 9];
%!Sides{Front} = [Red xx 9];
...
}
}

We have a single attribute, a Hash called %.Sides; Each key corresponds to one of the Enum sides. The value is a 9-element array of Colors. Each element on the array corresponds to a position on the cube. With white on top and red in front as the default, the colors and cell positions are shown here with the numbers & colors. (White is Up, Red is Front)

         W0 W1 W2
         W3 W4 W5
         W6 W7 W8
G2 G5 G8 R2 R5 R8 B2 B5 B8 O2 O5 O8
G1 G4 G7 R1 R4 R7 B1 B4 B7 O1 O4 O7
G0 G3 G6 R0 R3 R6 B0 B3 B6 B0 B3 B6
         Y0 Y1 Y2
         Y3 Y4 Y5
         Y6 Y7 Y8

The first methods I added were to do clockwise turns of each face.

method F {
self!rotate-clockwise(Front);
self!fixup-sides([
Pair.new(Up, [6,7,8]),
Pair.new(Right, [2,1,0]),
Pair.new(Down, [2,1,0]),
Pair.new(Left, [6,7,8]),
]);
self;
}

This public method calls two private methods (denoted with the !); one rotates a single Side clockwise, and the second takes a list of Pairs, where the key is a Side, and the value is a list of positions. If you imagine rotating the top of the cube clockwise, you can see that the positions are being swapped from one to the next.

Note that we return self from the method; this allows us to chain the method calls as we wanted in the original design.

The clockwise rotation of a single side shows a raw Side being passed, and uses array slicing to change the order of the pieces in place.

# 0 1 2 6 3 0
# 3 4 5 -> 7 4 1
# 6 7 8 8 5 2
method !rotate-clockwise(Side \side) {
%!Sides{side}[0,1,2,3,5,6,7,8] = %!Sides{side}[6,3,0,7,1,8,5,2];
}

To add the rest of the notation for the moves, we add some simple wrapper methods:

method F2 { self.F.F; }
method Fʼ { self.F.F.F; }

F2 just calls the move twice; Fʼ cheats: 3 rights make a left.

At this point, I had to make sure that my turns were doing what they were supposed to, so I added a gist method (which is called when an object is output with say).

say Cube::Three.new.U2.D2.F2.B2.R2.L2;
      W Y W
      Y W Y
      W Y W
G B G R O R B G B O R O
B G B O R O G B G R O R
G B G R O R B G B O R O
      Y W Y
      W Y W
      Y W Y

The source for the gist is:

method gist {
my $result;
$result = %!Sides{Up}.rotor(3).join("\n").indent(6);
$result ~= "\n";
for 2,1,0 -> $row {
for (Left, Front, Right, Back) -> $side {
my @slice = (0,3,6) >>+>> $row;
$result ~= ~%!Sides{$side}[@slice].join(' ') ~ ' ';
}
$result ~= "\n";
}
$result ~= %!Sides{Down}.rotor(3).join("\n").indent(6);
$result;
}

A few things to note:

  • use of .rotor(3) to break up the 9-cell array into 3 3-element lists.

  • .indent(6) to prepend whitespace on the Up and Down sides.
  • (0,3,6) >>+>> $row, which increments each value in the list

The gist is great for stepwise inspection, but for debugging, we need something a little more compact:

method dump {
gather for (Up, Front, Right, Back, Left, Down) -> $side {
take %!Sides{$side}.join('');
}.join('|');
}

This iterates over the sides in a specific order, and then uses the gather take syntax to collect string representations of each side, then joining them all together with a |. Now we can write tests like:

use Test; use Cube::Three;
my $a = Cube::Three.new();
is $a.R.U2...R....U2.L.U..U.L.dump,
'WWBWWWWWB|RRRRRRRRW|BBRBBBBBO|OOWOOOOOO|GGGGGGGGG|YYYYYYYYY',
'corners rotation';

This is actually the method used in the final step of the algorithm. With this debug output, I can take a pristine cube, do the moves myself, and then quickly transcribe the resulting cube state into a string for testing.

While the computer doesn't necessarily need to rotate the cube, it will make it easier to follow the algorithm directly if we can rotate the cube, so we add one for each of the six possible turns, e.g.:

method rotate-F-U {
self!rotate-clockwise(Right);
self!rotate-counter-clockwise(Left);
# In addition to moving the side data, have to
# re-orient the indices to match the new side.
my $temp = %!Sides{Up};
%!Sides{Up} = %!Sides{Front};
self!rotate-counter-clockwise(Up);
%!Sides{Front} = %!Sides{Down};
self!rotate-clockwise(Front);
%!Sides{Down} = %!Sides{Back};
self!rotate-clockwise(Down);
%!Sides{Back} = $temp;
self!rotate-counter-clockwise(Back);
self;
}

As we turn the cube from Front to Up, we rotate the Left and Right sides in place. Because the orientation of the cells changes as we change faces, as we copy the cells from face to face, we also may have to rotate them to insure they end up facing in the correct direction. As before, we return self to allow for method chaining.

As we start testing, we need to make sure that we can tell when the cube is solved; we don't care about the orientation of the cube, so we verify that the center color matches all the other colors on the face:

method solved {
for (Up, Down, Left, Right, Back, Front) -> $side {
return False unless
%!Sides{$side}.all eq %!Sides{$side}[4];
}
return True;
}

For every side, we use a Junction of all the colors on a side to compare to the center cell (always position 4). We fail early, and then succeed only if we made it through all the sides.

Next I added a way to scramble the cube, so we can consider implementing a solve method.

method scramble {
my @random = <U D F R B L>.roll(100).squish[^10];
for @random -> $method {
my $actual = $method ~ ("", "2", "ʼ").pick(1);
self."$actual"();
}
}

This takes the six base method names, picks a bunch of random values, then squishes them (insures that there are no dupes in a row), and then picks the first 10 values. We then potentially add on a 2 or a ʼ. Finally, we use the indirect method syntax to call the individual methods by name.

Finally, I'm ready to start solving! And this is where things got complicated. The first steps of the beginner method are often described as intuitive. Which means it's easy to explain… but not so easy to code. So, spoiler alert, as of the publish time of this article, only the first step of the solve is complete. For the full algorithm for the first step, check out the linked github site.

method solve {
self.solve-top-cross;
}
method solve-top-cross {
sub completed {
%!Sides{Up}[1,3,5,7].all eq 'W' &&
%!Sides{Front}[5] eq 'R' &&
%!Sides{Right}[5] eq 'B' &&
%!Sides{Back}[5] eq 'O' &&
%!Sides{Left}[5] eq 'G';
}
...
MAIN:
while !completed() {
# Move white-edged pieces in second row up to top
# Move incorrectly placed pieces in the top row to the middle
# Move pieces from the bottom to the top
}
}

Note the very specific checks to see if we're done; we use a lexical sub to wrap up the complexity – and while we have a fairly internal check here, we see that we might want to abstract this to a point where we can say "is this edge piece in the right orientation". To start with, however, we'll stick with the individual cells.

The guts of solve-top-cross are 100+ lines long at the moment, so I won't go through all the steps. Here's the "easy" section

my @middle-edges =
[Front, Right],
[Right, Back],
[Back, Left],
[Left, Front],
;
for @middle-edges -> $edge {
my $side7 = $edge[0];
my $side1 = $edge[1];
my $color7 = %!Sides{$side7}[7];
my $color1 = %!Sides{$side1}[1];
if $color7 eq 'W' {
# find number of times we need to rotate the top:
my $turns = (
@ordered-sides.first($side1, :k) -
@ordered-sides.first(%expected-sides{~$color1}, :k)
) % 4;
self.U for 1..$turns;
self."$side1"();
self.for 1..$turns;
next MAIN;
} elsif $color1 eq 'W' {
my $turns = (
@ordered-sides.first($side7, :k) -
@ordered-sides.first(%expected-sides{~$color7}, :k)
) % 4;
self.for 1..$turns;
self."$side1"();
self.U for 1..$turns;
next MAIN;
}
}

When doing this section on a real cube, you'd rotate the cube without regard to the side pieces, and just get the cross in place. To make the algorithm a little more "friendly", we keep the centers in position for this; we rotate the Up side into place, then rotate the individual side into place on the top, then rotate the Up side back into the original place.

One of the interesting bits of code here is the .first(..., :k) syntax, which says to find the first element that matches, and then return the position of the match. We can then look things up in an ordered list so we can calculate the relative positions of two sides.

Note that the solving method only calls to the public methods to turn the cube; While we use raw introspection to get the cube state, we only use "legal" moves to do the solving.

With the full version of this method, we now solve the white cross with this program:

#!/usr/bin/env perl6
use Cube::Three;
my $cube = Cube::Three.new();
$cube.scramble;
say $cube;
say '';
$cube.solve;
say $cube;

which generates this output given this set of moves (Fʼ L2 B2 L Rʼ Uʼ R Fʼ D2 B2). First is the scramble, and then is the version with the white cross solved.

      W G G
      Y W W
      Y Y Y
O O B R R R G B O Y Y B
R G O B R R G B G W O B
Y B B R O W G G G W W O
      W W O
      Y Y O
      B R R

      Y W W
      W W W
      G W R
O G W O R Y B B G R O G
Y G G R R B R B Y R O G
O O R Y O W O O R W Y B
      G G B
      B Y Y
      Y B B

This sample prints out the moves used to do the scramble, shows the scrambled cube, "solves" the puzzle (which, as of this writing, is just the white cross), and then prints out the new state of the cube.

Note that as we get further along, the steps become less "intuitive", and, in my estimation, much easier to code. For example, the last step requires checking the orientationof four pieces, rotating the cube if necessary, and then doing a 14-step set of moves. (shown in the test above).

Hopefully my love of cubing and Perl 6 have you looking forward to your next project!

I'll note in the comments when the module's solve is finished, for future readers.

Day 1 – Consuming GitHub Webhooks

Welcome Back

Welcome to the first Advent post of 2016; this marks almost the first anniversary of the release of the Perl 6 specification. Rakudo Perl 6 has released a compiler for over 100 months (even before the release of the actual specification). The releases this year each come with bug fixes, speed improvements, and occasionally new features that will probably become part of the next version of the specification.

For the first advent article this year, I wanted to share an experience where I was able to use Perl 6 for one of the strengths that it inherited from its older sibling: whipuptitude (coined by Larry – “the aptitude for whipping things up”). When I was given the task of getting something working quickly at $DAYJOB recently, it was the tool I reached for.

Consuming Github Webhooks

The task was to setup a tool that would, for a variety of projects in github enterprise, create a docker image every time someone pushed a commit to the mainline development branch.

Start Listening

If you have a github project, you may have seen in the settings that you can setup a webhook – an HTTP call that is triggered after certain events occur in the repository. Let’s create a test repository in github (or github enterprise if you have access to one at work); For the public github, you can go to https://github.com/new, pick a new repository name, select “Initialize this repository with a README”, and click “Create Repository”

Now let’s turn on a webhook. (For this to work, you’ll need to be able to make an http connection from the github/enterprise to your local machine.) Go to the repository’s settings, pick Webhooks, then “Add webhook”. Note that the webhook says that it will be a POST request, and can send JSON: we’ll need that later.

Pick a port, e.g. 7890, so the payload URL will be something like: http://somemachine.your.co:7890/build

For this proof of concept, we just need git push events (the default). Click “Add webhook” to finalize this hook. Now commit something to your test repository. You can see the error the webhook reports – it couldn’t connect. So, our first step is to be able to pick up when the webhook calls.

Looking through the Perl 6 ecosystem (http://modules.perl6.org/), we find Bailador, a Perl 6 play on 5’s Dancer, that will let us run a local HTTP server. After installing that module with zef or panda, we can write a barebones program that will let us listen to all POST requests on a particular port. We use the .* regex to indicate all paths, and give it an anonymous sub as a callback. The callback outputs a line when called, then tells github that everything is “OK”. The last line tells Bailador to listen on that port.

use Bailador;
post /.*/ => sub {
    say "Hello?";
    "OK";
}
baile(7890);

Now if we run this script, it blocks, waiting for connections. Push another commit to your git repo, and now you can see that everytime you push, our script wakes up, Bailador notes that it received a POST to the “/build” URL, and our callback emits “Hello?”, and then waits again.

What did you say?

Our next step is to see what data github is actually sending us: easy enough, let’s dump out the HTTP request that was shipped, using a sigilless variable that is made available to us in a Bailador routing callback. Let’s also replace our previous regex with the path “/build” to match the URL we gave the webhook.

use Bailador;
post '/build' => sub {
    say request.body;
    "OK";
}
baile(7890);

After another push, we can verify that github is sending us a body consisting of JSON, just like it said it would. Digging into the ecosystem again, we pick one of the JSON modules, and use that to convert the JSON string we get into an actual Perl 6 data structure:

use Bailador;
use JSON::Fast;
post '/build' => sub {
    say (from-json request.body).keys;
    "OK";
}
baile(7890);

Pushing now gives us a list of the top level keys of the JSON structure. The JSON from the request body is converted to a hash with the module’s from-json routine, and then we print just the keys from that structure. Now we can actually start processing some data.

What do I want?

We’re going to need to know what repository we’re working with, where to checkout a copy, and which branch it was on. We’ll ignore the revision and just assume we need to grab the latest version each time we get a push. Looking at the output from the previous dumps above, we can pull out the specific information we need from the data.

use Bailador;
use JSON::Fast;
post '/build' => 
    my $data = from-json request.body;

    my $repo      = $data<repository><full_name>;
    my $clone-url = $data<repository><ssh_url>;
    my $branch    = $data<ref>.split('/')[*-1];

    dd $repo, $clone-url, $branch; 
    "OK";
}
baile(7890);

The $data<key> syntax lets us index into $data with a literal key. For the branch, we split up the refs/heads/master into chunks by / and then pick the last entry. Note that we use Rakudo’s un-specced helper routine dd to pretty print the three variables to make sure we’re pulling the right data.

Now we have enough data to pull a copy of the repo and do some work! If the branch in question is the right one, we’re going to want to get a checkout and do a build on the HEAD of that branch. While for $DAYJOB, we did a docker build and pushed that up to a registry, for this sample, we’ll just checkout a copy.

    use Bailador;
    use JSON::Fast;
    post '/build' => sub {
        my $data = from-json request.body;

        my $repo      = $data<repository><full_name>;
        my $clone-url = $data<repository><ssh_url>;
        my $branch    = $data<ref>.split('/')[*-1];

        return "OK" if $branch ne "master";

        qqx/rm -rf "$repo"/;
        qqx/git clone --branch $branch --depth 1 "$clone-url" "$repo"/;


        "OK";
    }
    baile(7890);

The callback now bails out early if the branch we were called on isn’t master. We shell out to remove the repo directory (as coke on github, my tests in writing this article were on coke/demo), and then do minimal git clone of the appropriate branch. Since we’re shelling out, you can easily add your actual build/upload step here as well.

Too Slow?

Once we added the checkout step, you may have noticed that github complains of a timeout. Because we don’t care to report back to github if a particular revision successfully built or not (we’re just creating a build), there’s no need to wait for it to complete. Perl 6 makes this very easy. Our final version has just one more word and a block:

use Bailador;
use JSON::Fast;
post '/build' => sub {
    my $data = from-json request.body;

    my $repo      = $data<repository><full_name>;
    my $clone-url = $data<repository><ssh_url>;
    my $branch    = $data<ref>.split('/')[*-1];

    return "OK" if $branch ne "master";

    start {
        qqx/rm -rf "$repo"/;
        qqx/git clone --branch $branch --depth 1 "$clone-url" "$repo"/;
    }

    "OK";
}
baile(7890);

The start block here begins an asynchronous request. The POST callback skips over this and immediately returns “OK” to the github webhook, while Perl 6 queues our clone (and eventually our build) to run in the background.

What Next?

We were able to implement this solution without knowing much about webhooks up front (just that they are HTTP POST requests), iterating our Perl 6 script as we were able to see more information at each step.

Granted, this was the bare minimum to get things working for a project at my $DAYJOB – you might have more requirements, or have more time to play. Some experiments you could do to extend this script:

  • Add HTTPS support
  • Change the git checkout to keep a cached version that uses ‘clone –mirror’, and then fetches instead of cloning each time.
  • Create a local YAML config file that allows you to change the branch that triggers the build for each repository. (Or a DB connection, or…)
  • Make the handler smart enough to process each commit rather than just the latest commit when we are called.
  • Customize the script to actually do a build for your project.

Cheers!

Christmas announcement, two.

As we prepare to cut the first monthly release of the Rakudo Perl 6 compiler of 2016, we wanted to specifically thank all the individuals, companies, and organizations who donated money, services, equipment, or their employee’s time to the effort.  This includes efforts going back to 2000 over multiple Perl 6 related projects. As with our developer list on the Christmas release, our sincere apologies to anyone who was accidentally left off this list.

  • ActiveState
  • BBC
  • Blackstar
  • Booking.com
  • craigslist
  • Richard Dice of Toronto.pm
  • Dijkmat
  • Edument
  • ETH Zürich
  • Google Summer of Code
  • Ian Hague
  • Manning Publications
  • Morgan Stanley
  • Mozilla
  • NLNet
  • noris network AG
  • O’Reilly Media
  • Frédéric Schütz of the Swiss Institute of Bioinformatics
  • Stonehenge Consulting
  • The Perl Foundation
  • TPF’s Perl 6 Core Development Fund sponsors
  • VA Linux
  • WenZPerl
  • Fritz Zaucker of Oetiker+Partner AG

Again, thank you all for your support.

Christmas is here.

On behalf of the Rakudo development team, I’m proud to announce the Christmas release (December 2015) of Rakudo Perl 6 #94 “коледа”. Rakudo is an implementation of Perl 6 on the Moar Virtual Machine[^1].

This is the Christmas release of Rakudo Perl 6. This version of the compiler targets the v6.c “Christmas” specification of the Perl 6 language. The Perl 6 community has been working toward this release over the last 15 years. Together, we’ve built a language that:

  • Retains the core values of Perl: expressiveness, getting the job done, taking influences from natural language, and pushing the boundaries of language design
  • Has clean, modern syntax, rooted in familiar constructs but revisiting and revising the things that needed it
  • Is truly multi-paradigm, enabling elegant object-oriented, functional, procedural, and concurrent programming
  • Serves as a great glue language, allowing for easy calling of C/C++ (using NativeCall) and staying compatible with Perl 5 (via Inline::Perl5).
  • Provides composable constructs for working with asynchronous data and parallel computations
  • Dramatically reforms and sets a new standard in regex syntax, which scales up to full grammars – powerful enough to parse Perl 6 itself
  • Has outstanding Unicode support, with strings working at grapheme level
  • Values lexical scoping and encapsulation, enabling easy refactoring
  • Is extensible through meta-object programming, user-defined operators, and traits

The tag for this release is “коледа”[^2], a slavic word for an ancient winter festival that has been incorporated into Christmas. We hope you join us in our celebration of getting our Christmas release shipped!

While we are extremely happy to ship an official Perl 6 release, this is not the end of Rakudo’s development. We will continue to ship monthly releases, which will continue to improve performance and our users experience. We’ll also continue our work on the specification, with feedback from the community.

To be clear on that point, this Rakudo release is not considered the primary deliverable for this Christmas; it is the language specification, known as “roast” (Repository Of All Spec Tests), that is considered the primary deliverable. The specification tests that define this 6.c version[^3] of the language are now frozen, and we hope it will be quite some time before we feel obligated to define a 6.d (Diwali) version of the language.

This Rakudo release targets those tests (over 120 thousand of them), and passes them all on at least some architectures when the moon is in the right phase. But Rakudo itself is not frozen. There is still plenty of work ahead for us to improve speed, portability, and stability. Do not expect the level of perfection that you see in established products. This is essentially a .0 release of a compiler. We do not claim an absence of bugs or instabilities. We do not claim the documentation is complete. We do not claim portability to many architectures. We do not claim that all downstream software will work correctly. Think of it as a first kernel release, and now we get to build and port various distributions based around that kernel.

What we do claim is that you now have a stable language specification, and you can enjoy getting some stuff done with Perl 6 without us breaking it every month—as long as you stick to the features that are actually tested in the test suite, that is. Please note that any “feature” you discover that is not tested in the test suite is considered fair game for change without notice.

Have the appropriate amount of fun!

The tarball for this release is available from http://rakudo.org/downloads/rakudo/.

Please note: This announcement is not for the Rakudo Star distribution[^4] — it’s announcing a new release of the compiler and the specification. For the latest Rakudo Star release, see http://rakudo.org/downloads/star/. A Christmas-based version will be available soon.

In addition to being our Christmas release, this is yet another monthly compiler release; Some of the changes that are new in release are outlined below:

New in 2015.12:

  • Fixed size and multi-dimensional typed and native arrays
  • Greatly overhauled module loading and installation, including handling precompilation at module installation time in Rakudo
  • while/until loops can now return lists of values
  • We now catch many more kinds of “Useless use of X in sink context”
  • A number of convenient Unicode equivalents were introduced
  • Superscripts can now be used for integer powers
  • Non-digit unicode characters with a numeric value (½ and such) can now be used for that numeric value
  • There is a new “approximately equal” operator
  • Add support for USAGE argument help
  • Provide tau constant (also: τ)
  • Can now use channels with supply/react/whenever
  • Bool is now a proper enum
  • Supply/Supplier improvements
  • Use of EVAL now requires a declaration of ‘use MONKEY-SEE-NO-EVAL’
  • Make pack and unpack experimental
  • Dynamic variables are now visible inside start { … } blocks
  • Autoincrements on native ints are now faster than on Int
  • The ~~ operator can now chain with other comparisons in many circumstances Various numeric operations now return overflow/underflow failures instead of wrong value
  • The :ss, :ii, and :mm options to s/// now all work together

This is only a partial list of the changes in this release. For a more
detailed list, see “docs/ChangeLog”.

The development team thanks all of our contributors and sponsors for making Rakudo Perl possible, as well as those people who worked on the design docs, the Perl 6 test suite, MoarVM and the specification. Additionally, the Pugs, Parrot, and Niecza projects were all instrumental with their contributions to the specification and the community.

The following people contributed to the development of the Christmas release. We’ve gone back through the logs of all the various projects. Thanks to everyone who has worked to make this release happen over the past 15 years. We would also like to thank everyone who submitted bug reports or dropped in on the various forums to discuss things. Finally, we’d like to extend a special thanks to everyone who we accidentally left out of this list.

Gisle Aas, abcxyzp, Chuck Adams, Colin Paul Adams, Rod Adams, C.J. Adams‑Collier, David H. Adler, Chirag Agrawal, Amir E. Aharoni, Bilal Akhtar, Julian Albo, Alekssasho, alexghacker, Paco Alguacil, Brandon S Allbery, Geir Amdal, Markus Amsler, Paul C. Anagnostopoulos, Nikolay Ananiev, anatolyv, andras, Saleem Ansari, Joji Antony, Tomoki Aonuma, Syed Uzair Aqeel, arathorn, Arcterus, Kodi Arfer, Daniel Arbelo Arrocha, ash, Ted Ashton, Arnaud Assad, atroxaper, Ori Avtalion אורי אבטליון, Auzon, Greg Bacon, Ivan Baidakou, Alex Balhatchet, Szabó, Balázs, Amir Livine Bar‑On עמיר ליבנה בר‑און, Luca Barbato, Mattia Barbon, Ann Barcomb, Christian Bartolomäus, Alex “Skud” Bayley, bcmb, Jody Belka, Shachaf Ben‑Kiki, Andrei Benea, benedikth, Zev Benjamin, benmorrow, Kevan Benson, Martin Berends, Anton Berezin, Arthur Bergman, Anders Nor Berle, bestian, Peter Bevan, Mark Biggar, Carlin Bingham, Ævar Arnfjörð Bjarmason, J. David Blackstone, Ronald Blaschke, Ingo Blechschmidt, bloonix, blwood, Kristof Bogaerts, Dan Bolser, Конрад Боровски, Christopher Bottoms, Gonéri Le Bouder, Jos Boumans, Brad Bowman, Matt Boyle, bpetering, H.Merijn Brand, Terrence Brannon, Gwern Branwen, Stig Brautaset, Herbert “lichtkind” Breunung, bri, brian_d_foy, Fernando Brito, Geoffrey Broadwell, Leon Brocard, Benjamin Brodfuehrer, Samuel Bronson, Dan Brook, Nathan C. Brown, Roger Browne, Philippe Bruhat (BooK), David Brunton, Corwin Brust, Klaus Brüssel, Lucas Buchala, buchetc, Christoph Buchetmann, Norbert Buchmuller, Buddha Buck, Alexandre Buisse, Tim Bunce, Bryan Burgers, Sean M. Burke, Matthew Byng‑Maddick, András Bártházi, Jürgen Bömmels, Caelum, Aldo Calpini, Edward Cant, David Cantrell, Carlin, Michael Cartmell, Hezekiah Carty, Nuno ‘smash’ Carvalho, Marcelo Serra Castilhos, Piers Cawley, cdavaz, cdpruden, Gianni Ceccarelli, cfourier, Marc Chantreux, Mitchell N Charity, Oliver Charles, Vasily Chekalkin, Yuan‑Chen Cheng 鄭原真, Daniel Chetlin, Hsin‑Chan Chien 簡信昌, N. Hao Ching, Joshua Choi, Elizabeth Cholet, David Christensen, chuck, cjeris, Nicholas Clark, Steve Clark, Jeff Clites, cmarcelo, cmeyer, Paul Cochrane, Daniel Colascione, Jason Cole, Will “Coke” Coleda, Sylvain Colinet, cono, Tim Conrow, Géraud Continsouzas, Damian Conway, Neil Conway, Stuart Cook, David Corbin, Deven T. Corzine, cosmicnet, Timothy Covell, Beau E. Cox, Simon Cozens, Philip Crow, cspenser, Franck Cuny, Tyler Curtis, David Czech, Daenyth, Dagur, Ritz Daniel, darkwolf, Chris Davaz, David Warring, Justin DeVuyst, Daniel Dehennin, Akim Demaille, Detonite, Lars “daxim” Dieckow 迪拉斯, Matt Diephouse, Bob Diertens, Wendy “woolfy” van Dijk, Jeffrey Dik, John M. Dlugosz, dimid, diotalevi, Hans van Dok, Chris Dolan, Mark Dominus, Bryan Donlan, Andy Dougherty, Dave Doyle, drKreso, dr_df0, dudley, Jonathan Scott Duff, dug, Lee Duhem, Darren Duncan, Andrew Egeler, Havard Eidnes, Nelson Elhage, Fitz Elliott, Alex Elsayed, Jay Emerson, Aankhola Encorporated, ennio, Enveigler, Jon Ericson, Shae Matijs Erisson, Eryq, Mike Eve, Pat Eyler, Aaron Faanes, Kevin Falcone, David Farrell, Angel Faus, Jason Felds, Paul Fenwick, Jose Rodrigo Fernandez, Nelson Ferraz, Adriano Ferreira, João Fernando Ferreira, Chris Fields, Caio Marcelo de Oliveira Filho, Steve Fink, Shlomi “rindolf” Fish שלומי פיש, Mark Leighton Fisher, Scott Fitzenrider, Dudley Flanders, Richard Foley, Vincent Foley, Julian Fondren, Ruben Fonseca, David Formosa, Karl Forner, Solomon Foster, Chaddaï Fouché, Lloyd Fournier, Michael Fowler, Matt Fowles, franck, Austin Frank, Carl Franks, Kent Fredric, Chaim Frenkel, Piotr Fusik, gabriele, John Gabriele, Christoph Gärtner, Martin von Gagern, Felix Gallo, Salvador Ortiz Garcia, Rafaël Garcia‑Suarez, Joshua Gatcomb, Jerry Gay, gcomnz, Jonathan Gentle, iVAN Georgiev, Brian Gernhardt, Bram Geron, Alessandro Ghedini, Imran Ghory, Peter Gibbs, Tanton Gibbs, Brad Gilbert (b2gills), Karl Glazebrook, Nick Glencross, Mark Glines, Flávio S. Glock, Jason Gloudon, Simon Glover, gnuvince, Garrett Goebel, Jeffrey Goff, Mikhael Goikhman, Benjamin Goldberg, Arcady Goldmints‑Orlov, Dimitry Golubovsky, Gerard Goossen, Goplat, Alex Gough, Léo Grange, Chad “Exodist” Granum, Kenneth A Graves, Bruce Gray, Nathan Gray, Mark Grimes, Lucien Grondin, Rolf Grossmann, David Grove, Marcel Grünauer, Daniel Grunblatt, Uri Guttman, gwern, Swaroop C H, Richard Hainsworth, Roger Hale, John “ab5tract” Haltiwanger, Nigel Hamilton, Eric Hanchrow, Sterling Hanenkamp, Ask Bjørn Hansen, Christian “chansen” Hansen, Eirik Berg Hanssen, Samuel Harrington, Trey Harris, John Harrison, Carsten Hartenfels, Richard Hartmann, Kyle Hasselbacher, Austin Hastings, Carl Hayter, Florian Helmberger, Gordon Henriksen, Felix Herrmann, Peter Heslin, Fred Heutte, Jarkko Hietaniemi, Michael H. Hind, Joshua Hoblitt, Zack Hobson, Eric Hodges, Rob Hoelz, Masahiro Honma, Lyle Hopkins, JD Horelick, Jeff Horwitz, Chris Hostetter, Laurens Van Houtven, Jeremy Howard, Yiyi Hu 鹄驿懿, Benedikt Huber, Brad Hughes, Sterling Hughes, Tom Hughes, Tristan Hume, Donald Hunter, Douglas Hunter, Juan Francisco Cantero Hurtado, Kay‑Uwe ‘kiwi’ Hüll, Ran Eliam, Alin Iacob, Ibotty, ibrown, ihrd, Roland Illing, Jan Ingvoldstad, Joshua Isom, isop, Ivan_A_Frey, ivanoff, Akash Manohar J, jafelds, Robert G. Jakabosky, jamesrom, S. A. Janet, jani, Heiko Jansen, Stuart Jansen, Jarrod, Jedai, Chris Jepeway, Chris Jeris, Dagur Valberg Johannsson, Erik Johansen, Paul Johnson, johnspurr, Isaac Jones, Norman Nunley, Jr, Yoshikuni Jujo, Brian S. Julin, Josh Juran, Michal Jurosz, David KOENIG, Prakash Kailasa, Shoichi Kaji, Daniel Kang, Isis Kang, Chia‑Liang Kao 高嘉良, Dmitry Karasik, Luben Karavelov, Amir Karger, Offer Kaye, Bruce Keeler, James E Keenan, Cole Keirsey, Adam Kennedy, Matt Kennedy, Shane Kerr, khairul, Mikhail Khorkov, Krzysztof Kielak, Andres Kievsky, Daehyub Kim, Rob Kinyon, Oleg Kiselyov, OOLLEY kj, Martin Kjeldsen, Thomas “domm” Klausner, Zohar “lumi” Kelrich זהר קלריך/卡卓哈, Damian Miles Knopp, Dan Kogai 小飼弾, Yuval “nothingmuch” Kogman יובל קוג’מן, Tomasz Konojacki, Vadim Konovalov, Nick Kostirya, Matt Kraai, Thomas Kratz, Adrian Kreher, John van Krieken, Matthias Krull, Bradley M. Kuhn, Bob Kuo, Colin Kuskie, Kamil Kułaga, Sage LaTorra, Brent Laabs, laben, Johannes Laire, Markus Laire, Fayland Lam 林道, Mike Lambert, lanny, Leo Lapworth, last.of.the.careless.men, Bart Lateur, Jia‑Hong Lee, Lola Lee, Jonathan Leffler, Tobias Leich, lembark, Mark Lentczner, Moritz A Lenz, Jean‑Louis Leroy, Andy Lester, Jonathan “Duke” Leto, Vladimir Lettiev, Mike Li 李曉光, Stefan Lidman, Yung‑Chung Lin 林永忠, Glenn Linderman, Vladimir Lipsky, Zach Lipton, Stevan Little, Kang‑Min Liu 劉康民, Skip Livingston, David M. Lloyd, Daniel Lo, Peter Lobsinger, Andres Löh, Nick Logan, Eric Lubow, Nolan Lum, Peter Lunicks, LylePerl, Ian Lynagh, lysdexsik, Kiran Kumar M., Jerry MacClure, Noel Maddy, Christopher J. Madsen, Abhijit A. Mahabal, Max Maischein, Peter Makholm, Ujwal Reddy Malipeddi, malon, Christopher Malon, Dagfinn Ilmari Mannsåker, Michael Maraist, Roie Marianer רועי מריאנר, markmont, Simon Marlow, martin, Paolo Martini, Ilya Martynov, Jaume Martí, James Mastros, Michael J. Mathews, Vyacheslav Matjukhin, Tokuhiro Matsuno, mattc, Mike Mattie, Elizabeth “lizmat” Mattijsen, Вячеслав Матюхин, Markus Mayr, Josh McAdams, Martin McCarthy, Mark McConnell, Steven McDougall, John McNamara, Scott McWhirter, mdinger, Olivier “dolmen” Mengué, Kevin Metcalf, Patrick R. Michaud, mimir, mjreed, Tom Moertel, Michael Mol, Paolo Molaro, Shawn M Moore, Brandon Michael Moore, Alexander Moquin, Ion Alexandru Morega, Dino Morelli, Kolia Morev, Zach Morgan, mr_ank, Alex Munroe, muraiki, Paweł Murias, mvuets, Steve Mynott, mzedeler, Carl Mäsak, naesten, Tim Nelson, Ailin Nemui, Ingy döt Net 應吉大聶, David Nicol, Faye Niemeyer, Nigelsandever, Karl Rune Nilsen, Salve J. Nilsen, Per Christian Nodtvedt, Ben Noordhuis, Paweł Nowak, Norman Nunley, Tony O’Dell, יהושע “שי” אוחיון, Clayton O’Neill, Stefan O’Rear, Sean O’Rourke, Matt Oates, Tony Olekshy, Martin Olsen, Dmitriy Olshevskiy, Dave Olszewski, Nelo Onyiah, William Orr, Jon Orwant, Andrei Osipov, Christoph Otto, Pawel Pabian, Walter Pallestrong, Luke Palmer, Bartłomiej Palmowski, Pancake, Paolo, Marton Papp, Andrew Parker, Roman M. Parparov, Anthony Parsons, Mike Pastore, Nova Patch, Timo Paulssen, Tony Payne, Stéphane “cognominal” Payrard, Slava Pechenin, Gael Pegliasco, Stéphane Peiry, Felipe Pena, Nayden Pendov, Wenzel P. P. Peppmeyer, François Perrad, Markus Peter, Ben Petering, Steve Peters, Tim Peterson, Ronny Pfannschmidt, Clinton A. Pierce, Jerrad Pierce, Thilo Planz, plunix, pmakholm, Curtis ‘Ovid’ Poe, Gerd Pokorra, Peter Polacik, Flavio Poletti, Kevin Polulak, John Porter, Ryan W. Porter, Stephen P. Potter, Philip Potter, Adam Preble, premshree, Klāvs Priedītis, Adam Prime, Richard Proctor, Christopher Pruden, Kevin Puetz, Gregor N. Purdy, purl, Hongwen Qiu, Jerome Quelin, quester, Gerhard R., Peter Rabbitson, Florian Ragwitz, raiph, Matt Rajca, Marcus Ramberg, Claudio Ramirez, Prog Rammer, Allison Randal, David Ranvig, Lars Balker Rasmussen, Curtis Rawls, raydiak, Robin Redeker, Ted Reed, Jens Rehsack, Charles Reiss, Gabriele Renzi, Kenneth C. Rich, Jason Richmond, Ryan Richter, Sebastian Riedel, Dennis Rieks, Jens Rieks, Lanny Ripple, John Rizzo, rkhill, Andrew Robbins, Amos Robinson, Jonathan Rockway, Stefano Rodighiero, Andrew Rodland, Lindolfo Rodrigues, Bob Rogers, Dave Rolsky, David Romano, ron, Eric J. Roode, Garret Rooney, Garrett Rooney, David Ross, Andreas Rottmann, Brent Royal‑Gordon, Shmarya Rubenstein, Sam Ruby, Simon Ruderich, Daniel Ruoso, Jake Russo, ruz, Joseph Ryan, Gilbert Röhrbein, Sam S, s1n, sahadev, Patrick Abi Salloum, salty_horse, Chip Salzenberg, Shrivatsan Sampathkumar, Igor Rafael Sanchez‑Puls, Hugo van der Sanden, Thomas Sandlaß, Yun SangHo, sanug, Siddhant Saraf, Sasho, Andrew Savige, John Saylor, Matt Schallert, Bernhard Schmalhofer, Arthur Axel Schmidt, Ronald Schmidt, Michael Schroeder, Steven Schubiger, Steve “thundergnat” Schulze, Andreas Schwab, Randal L. Schwartz, Pepe Schwarz, Frederik Schwarzer, Calvin Schwenzfeier, Michael G. Schwern, Steffen Schwigon, Tom Scola, Ariel Scolnicov, Michael Scott, Peter Scott, Rick Scott, Stefan Seifert, Austin Seipp, Mark Senn, Filip Sergot, Seth Gold (Sgeo), William Shallum, Kris Shannon, shenyute, Aaron Sherman, Mark Shoulson, Ryan Siemens, Ricardo Signes, Hinrik Örn Sigurðsson, Jonathan Sillito, Miroslav Silovic, Steve Simmons, Alberto Manuel Brandao Simoes, John Siracusa, Arne Skjærholt, Barrie Slaymaker, Radek Slupik, Mike Small, Benjamin Smith, Melvin Smith, Tim Smith, Adrian White aka snarkyboojum, Richard Soderberg, SolidState, Vishal Soni, Syloke Soong, Shawn Sorichetti, Tadeusz Sośnierz, sue spence, Cory Spencer, Robert Spier, Michael Stapelberg, Edwin Steiner, stephenpollei, Michael Stevens, Don Stewart, Radu Stoica, Klaas‑Jan Stol, Alek Storm, David Storrs, Mark Stosberg, Jonathan Stowe, Cosimo Streppone, Jonathan Strickland, Pascal Stumpf, Su‑Shee, Sue, Laye Suen, Dan Sugalski, Mark Summerfield, Simon Sun, Cheng‑Lung Sung 宋政隆, Sunnavy, Samuel Sutch, svatsan, svn, Andrew Sweger, sygi, Sebastian Sylvan, Gábor Szabó, Bálint Szilakszi, Marek Šuppa, TOGoS, Arvindh Rajesh Tamilmani, Audrey Tang 唐鳳, Bestian Tang 唐宗浩, Adrian Taylor, Jesse Taylor, Philip Taylor, Kevin Tew, the_dormant, Marcus Thiesen, Adam Thomason, Richard Tibbetts, Carey Tilden, Marcel Timmerman, Leon Timmermans, tkob, John Tobey, Frank Tobin, Bennett Todd, Graham Todd, Leopold Tötsch, Daniel Toma, Nathan Torkington, Timothy Totten, John J. Trammell, Matt S. Trout, Nat Tuck, Adam Turoff, Ben Tyler, ujwal, Bernd Ulmann, Reini Urban, parrot user, uzair, VZ, Ilmari Vacklin, vamped, Wim Vanderbauwhede, vendethiel, David Vergin, Ted Vessenes, Sam Vilain, Cédric Vincent, Jesse Vincent, Jos Visser, John van Vlaanderen, vmax, Jeremy Voorhis, Martin Vorländer, Johan Vromans, Maxim Vuets, Juerd Waalboer, Mariano Wahlmann, Kevin Walker, Gloria Wall, Larry Wall, Lewis Wall, Michal J Wallace, John Paul Wallington, walter, Matthew Walton, Ting Wang, Xinyuan Wang, Andy Wardley, Bryan C. Warnock, wayland, Stephen Weeks, Zhen‑Bang Wei, Nick Wellnhofer, Shu‑Chun Weng, Danny Werner, Brian Wheeler, David E. Wheeler, Dave Whipp, Adrian White, Andrew Whitworth, Bart Wiegmans, Nathan Wiger, Brock Wilcox, Edwin Wiles, Bob Wilkinson, Chris ‘BinGOs’ Williams, Greg Williams, Josh Wilmes, Matthew Wilson, Ashley Winters, Brian Wisti, Dave Woldrich, Helmut Wollmersdorfer, Kevin J. Woolley, Jonathan Worthington, Kuang‑Che Wu 吳光哲, xenu, Liang‑Qi Xie 謝良奇, Xtreak, Gaal Yahas גל יחס, Thomas Yandell, Alvis Yardley, Thomas Yeh 葉志宏, Natan Yellin, yiyihu, Matt Youell, Tony Young, Shen, Yu‑Teh, Ilya Zakharevich, Ruslan Zakirov, Ahmad M. Zawawi, Michael A. D. Zedeler, zengargoyle, zeriod, Agent Zhang 章亦春, Jimmy Zhuo, Ziggy6, Rob Zinkov, Zoffix Znet

If you would like to contribute or find out more information, visit
http://perl6.org, http://rakudo.org/how-to-help, ask on the
perl6-compiler@perl.org mailing list, or ask on IRC #perl6 on freenode.

The next release of Rakudo (#95), is tentatively scheduled for 16 January 2016. A list of the other planned release dates is available in the “docs/release_guide.pod” file.

The development team appreciates feedback! If you’re using Rakudo, do get back to us. Questions, comments, suggestions for improvements, cool discoveries, incredible hacks, or any other feedback — get in touch with us through (the above-mentioned) mailing list or IRC channel. Enjoy!

[^1]: See http://moarvm.org/

[^2]: See https://en.wikipedia.org/wiki/Koliada

[^3]: See https://github.com/perl6/roast/tree/6.c

[^4]: What’s the difference between the Rakudo compiler and the Rakudo Star distribution?

The Rakudo compiler is a compiler for the Perl 6 language. Not much more.

The Rakudo Star distribution is the Rakudo compiler plus a selection of useful Perl 6 modules, a module installer, the most recent incarnation of the “Using Perl 6” book, and other software that can be used with the Rakudo compiler to enhance its utility. Rakudo Star is meant for early adopters who wish to explore what’s possible with Rakudo Perl 6 and provide feedback on what works, what doesn’t, and what else they would like to see included in the distribution.

Day 21 – Signatures

In today’s post, we’ll go through the basics of Perl 6’s subroutine signatures, which allow us to declare our parameters instead of coding them as we do in Perl 5.

In Perl 5, arguments to a sub are accessible via the @_ array, so if you want to access an argument to a sub, you can either access the array directly (which is typically only done where speed is a concern)…

use v5;
sub greet {
    print "Hello, " . @_[0] . "\n";
}

Or, more commonly, pull off any arguments as lexicals:

use v5;
sub greet {
    my $name = shift @_;
    print "Hello, $name\n";
}

Positionals

Perl 6 lets you declare required positional arguments for your subs as part of the signature:

use v6;
sub greet($name) {
    print "Hello, $name\n";
}

Inside the sub, $name is available as a readonly alias to the passed in variable.

By default, parameters are required. If you try to call this function with no parameters as greet, you’ll get a compile time error:

===SORRY!===
CHECK FAILED:
Calling 'greet' requires arguments (line 1)
Expected: :($name)

You can make the parameter optional by adding a ? to the signature. Then you’ll have to examine the parameter to see if a defined value was passed in. Or, you can declare a default value to be used if none is passed in:

use v6;
sub guess($who, $what?) {
    # manually check to see if $what is defined
}

sub dance($who, $dance = "Salsa") {
    ...
}
dance("Rachael")
dance("Rachael", "Watusi")

Another way to handle optional arguments is to use multis. Multis are subs that can have multiple implementations that differ by their signature. When invoking a multi, the argument list used in the invocation is used to determine which version of the multi to dispatch to.

use v6;
multi sub dance($who, $dance) {
    say "$who is doing the $dance";
}
multi sub dance($who) {
    dance($who, "Salsa");
}

Types

The parameters in the previous section allow any type of value to be passed in. You can declare that only certain types are valid:

sub greet(Str $name) {
    say "hello $name";
}

Now hello("joe") will work as it did before. But hello(3) will generate a compile time error:

===SORRY!===
CHECK FAILED:
Calling 'greet' will never work with argument types (int) (line 1)
Expected: :(Str $name)

In addition to any of Perl 6 builtin types or user-built classes, you can also add programmatic checks inline in the signature with where clauses.

use v6;
multi odd-or-even(Int $i where * %% 2) { say "even" };
multi odd-or-even(Int $i) { say "odd"};

Note that we didn’t have to add a where clause to the second sub. The multi-dispatch algorithm will prefer the more detailed signature when it matches, but fallback to the less descriptive version when it doesn’t.

You can even use literal values as arguments. This style allows you to move some of your logic to the multi dispatcher.

use v6;
multi fib(1) { 1 }
multi fib(2) { 1 }
multi fib(Int $i) { fib($i-1) + fib($i-2) }

say fib(10);

Note that this isn’t very efficient… yet. Once the is cached trait for subs is implemented, we can use that to provide a builtin analog to Perl 5’s Memoize module.

Named

Perl 6 provides for named parameters; if the positionals are analogous to an array of parameters, the named arguments are like a hash. You use a preceding colon in the argument declaration, and then pass in a pair for each parameter when invoking the sub.

use v6;
sub doctor(:$number, :$prop) {
    say "Doctor # $number liked to play with his $prop";
}
doctor(:prop("cricket bat"), :number<5>);
doctor(:number<4>, :prop<scarf>);

Note that the order the parameters are passed in doesn’t matter for named parameters.

If you have a variable of the same name in the calling scope, you can simplify the calling syntax to avoid creating an explicit pair.

use v6;
my $prop = "fez";
my $number = 11;
doctor(:$prop, :$number)

You can also use different names internally (using the expanded pair syntax) for the named arguments. This allows you to use different (simplified or sanitized) versions for the caller.

use v6;
sub doctor(:number($incarnation), :prop($accoutrement)) {
    say "Doctor # $incarnation liked to play with his $accoutrement";
}
my $number = 2;
my $prop = "recorder";
doctor(:$number, :$prop)

Slurpy

To support functions like sprintf, we need to be able to take a variable number of arguments. We call these args “slurpy”, since they “slurp” up the arguments.

# from rakudo's src/core/Cool.pm
sub sprintf(Cool $format, *@args) {
    ...
}

When invoking this sub, the first argument must be of type Cool (kind of a utility supertype), and all the remaining positional arguments are slurped up into the @args variable. The preceding * indicates the slurp.

Symmetrically, we can also slurp up named arguments into a hash.

# from rakudo's src/core/control.pm
my &callwith := -> *@pos, *%named {
    ...
}

This snippet introduces to the pointy block syntax for anonymous subs. The -> begins the declaration, followed by the signature (without parentheses), and finally the sub definition in the block. The signature here shows all the positionals ending up in *@pos, and all the named arguments in %named.

This also shows that positionals and named arguments can be combined in the same sub.

Methods

Method and sub declarations are virtually identical. All the parameters mentioned so far are usable in methods.

The main difference is that methods can be passed an invocant (the object associated with the method call), and when you define the sub, you have the opportunity to name it. It must be the first parameter if present, and is marked with a trailing :.

use v6;
method explode ($self: $method) {...}

Note that there is no comma separating these the invocant and the first positional. In this context, the colon functions as a comma.

Parameter Traits

Each parameter can additionally specify a trait that changes the behavior of that parameter:

  • is readonly – this is the default behavior for a parameter; the sub cannot modify the passed in value.
  • is rw – the argument can be modified. Forces the argument to be required.
  • is copy – the sub gets a modifiable copy of the original value.

More Information

Check out Synopsis #6 for more information, and the roast test suite for more examples – any of the directories starting with S06-.

Day 13 – Roasting Rakudo Star

Roasting Rakudo Star

When is Perl 6 going to be Ready? We get this question a lot in the Perl 6 community, and the answer is never
as simple as we or the inquirers would like.

One part of the answer involves the specification; When we have an implementation that passes all of the tests marked “perl 6.0”, that will be a Perl 6.

Many people think of the specs as the Synopses, but Patrick Michaud makes a good point that the specification is really more about the tests.

Thus it was recognized early on (in Synopsis 1) that acceptance tests provide a far more objective measure of specification conformance than an English description. There are likely things that need to be “spec” that cannot be fully captured by testing… but I still believe that the test suite should be paramount.

Every language feature must have corresponding spec tests. Trying to find a test? Tests are broken up first by Synopsis (which themselves follow the numbering scheme of the Camel chapters), with multiple directories broken out by a group of features, then individual tests. For example, Synopsis 4 (S04) is about Blocks and Statements, including phasers. So to find the tests for the BEGIN phaser, you’ll want S04-phasers/begin.t in the roast suite.

We call the specification tests roast to follow in the tradition of “smoke test”, and also because TimToady can’t resist a punny backronym: “Repository Of All Spec Tests’.

Each of these files tries to thoroughly test something from the Synopsis including a lot of edge cases that aren’t necessarily mentioned in the prose. This goes to Patrick’s point about the tests being the more canonical answer about what the spec is.

Are we there yet?

Just a little further… 

So, how can we use these tests to determine if we’re done?

Each time a developer adds a feature or a language designer documents something in the Synopses, the team must add corresponding tests in roast – each change in the Synopses text may potentially increase the gap between prose and tests, and we have to regularly verify that both sets of documents are in agreement.

The tests even turn out to inform the prose, because they are concrete code – if you cannot write a coherent test, not just because it isn’t implemented in one of the compilers yet, but because it’s inefficient or breaks other tests, this will in turn require changes to the Synopses. Over the course of Perl 6’s development, compiler authors have pushed back on the specification in this way.

Before the compiler author checks in any code, ideally they should run the full spectest suite (roast) to insure not only their new tests work, but also that nothing else broke. This can be time consuming, so it’s possible they might just run a few test files for that particular feature or Synopsis.

So, despite best efforts, failing tests might be introduced. Even if you run all of roast, something might work on your compiler, but might have problems on another. Or another VM, or another OS, or hardware, or… So, there’s a need for regular testing that’s outside of the normal code/test/commit (or test/code/commit, if you like) workflow.

Speaking of other compilers and VMs, the current landscape of Perl 6 compilers is dominated by Rakudo. It has the most passing tests, two functioning backends (parrot and the JVM), a third on the way (MoarVM), and a possible fourth (JavaScript) landing in 2014. All of the virtual machines here support a wide variety of actual hardware and OSes. Niecza is another compiler, implemented in C#. It passes a substantial number of tests. We also test Pugs (targeting Haskell), but only for historical reasons.

Roasting

It can take a while to build and run the full roast suite for even a single compiler, and we are trying to keep track of between four and six at the moment (And that’s just for one architecture!) So, with limited infrastructure, rather than have a continual integration, we setup a single daily run that builds the latest version of every compiler from a fresh checkout using a shared copy of roast (so we can compare like to like), and then saves out the information into a github repository so we can see the current state. Github provides a nice interface for viewing the CSV data.

So, every day, we get a list of which test files are failing for each compiler/backend, which of the compilers is in the lead. When Rakudo/JVM started passing more spectests than Rakudo/Parrot, we were able see that immediately on the daily run.  Given the historical data available in the github repository, one could easily chart out things like (click to embiggen)

sixchart

number of passing tests per compiler/backend on the first of each month during 2013

Ecosystem 

But that’s just the compiler. The Rakudo team bundles a distribution called Rakudo Star (also spelled Rakudo *) that includes many modules from the Perl 6 ecosystem – kind of a mini-CPAN. This source distribution includes everything you need to build Rakudo from scratch and get a bunch of usable modules. Right now it’s Rakudo/Parrot, but work is being done for the distribution to support Rakudo/JVM and other backends.

We’ve had issues in the past where modules didn’t keep up with spec changes, and the person cutting the Star release would find issues with modules just before a release, causing delays.

Now we have a daily test that builds Rakudo Star, using the latest version of every module, Rakudo, NQP, and runs all the module tests, allowing us to catch any deprecation warnings or test errors as soon as they are made, rather than when we’re trying to cut a release. It’s plain text at the moment, but functions well as a warning indicator.

Test to the Future

Two other projects are in place for testing:

  • Colomon has an ad hoc process that tests all the modules in the ecosystem, not just star.
  • japhb has created a benchmark suite to help us prevent performance regressions across the various compilers. Here’s a video presentation.

Going forward, we need to setup and encourage the use of a smoke server so that we can take the daily runs on the testing platform, and combine them with the results from other platforms, compilers, etc.

Drop by the #perl6 channel on freenode or leave a comment here if you want to chat more about testing!

[Coke]