Quantum Tunneling

Introducing the new Perl Fisher site

Before I get on to the meat of the article, welcome to the new home of The Perl Fisher. I intend to cover both Perl 5 and Perl 6 programming here, but it’ll be mostly Perl 6 content because that’s the language I find the most fun. Please excuse the dust, I’m still very much settling into the new home, and the overall look of the site is bound to change while I play with the new toys available to me.

Defeating Thanos with Perl 6

Don’t worry, no spoilers here. We’re just going to talk about a little-known feature of Perl, the quantum-tunneling variable type. If you’ve worked with Perl 6 for any length of time, you’ve probably seen or written a class declaration that looks something like below.

class Point2D {
  has Real $.x;
  has Real $.y;
}

While the word ‘has’ does the real work, second-sigil syndrome strikes as well, in the shape of the ‘.’ between the scalar sigil ‘$’ and the variable name. Here it’s syntactical sugar for being an attribute name, but we can enlarge that ‘.’ to a ‘*’ and open up a world of possibilities.

When we add the ‘*’ sigil to a variable name, we turn that variable into one that can quantum tunnel between scopes and solve problems that you probably used to do with a global variable. You can read more about dynamic variables and how they differ from ordinary globals at The_*_twigil at docs.perl6.org.

Testing, testing

I’m working on a project to try to augment the Perl 6 grammar debugger with an emulator. The tools we have on CPAN and modules.perl6.org respectively are wonderful, but they’re limited because Perl 6 compiles grammar rules down to single methods, which is wonderful for speed, but makes it almost impossible to look into.

The grammar I’m writing isn’t important at the moment, but the testing part is. Below is a sample subtest that I’m writing for each term of a grammar that’s probably going to have ~50 terms by the time I’m done.

subtest 'binary-number', {
  subtest 'failing', {
    ok fails( '0b', 'binary-number' );
    ok fails( '3g', 'binary-number' );
  };

  is build-ast( '0101', 'binary-number' ), 5;
};

This tests the ‘binary-number’ rule to see if it properly fails on ‘0b’ and ‘3g’. ‘0b’ fails because it’s the prefix of a binary number, and ‘3g’ because neither 3 nor ‘g’ are binary digits. It also makes certain that ‘0101’ gets translated into the decimal number 5. All important when testing a grammar that parses … well, itself eventually. Oroborous redux, as it were.

Dry up, will you…

The test is simple, and straightforward. ‘0b’ should fail, ‘0101’ should be built into a node of an abstract syntax tree. But it’s got some flaws. It talks too much. See how ‘binary-number’ repeats itself? If I want to copy that, rename it to ‘hex-number’ and add a few changes, I have to copy the block, rename all the incidences of ‘binary-number’ to ‘hex-number’ and then fix the existing tests.

Thus I run the risk of forgetting to update the name ‘binary-number’. And there’s an even greater bugaboo there. If I don’t, the test won’t fail. Because the subtest doesn’t know that it’s supposed to be testing the ‘binary-number’ rule. There are a bunch of ways to solve this problem, of course, but for this post we’re going to use Ant-Man(tm).

Entering the Quantum Realm

I don’t want to do too much work here, I just want to get rid of the duplicate ‘binary-number’ entries. So, let’s take a look at what fails() does.

sub fails( Str $sample, Str $rule-name ) returns Bool {
  !?( $g.parse( $sample, :rule( $rule-name ) );
}

The ‘!?(…)’ casts $g.parse(…) to a Boolean and negates it, so if $g can’t parse the statement, it returns True. So, first let’s make $rule-name optional.

sub fails( Str $sample, Str $rule-name? ) returns Bool {
  !?( $g.parse( $sample, :rule( $rule-name ) );
}

Opening the wormhole

Now, we’re going to summon Ant-Man(tm). Remember earlier I mentioned that quantum variables use a wormhole? Well, we’re going to open one end of the wormhole right here in our fails() function, just like this.

sub fails( Str $sample, Str $rule-name? ) returns Bool {
  !?( $g.parse( $sample, :rule( $*ANT-MAN // $rule-name ) );
}

Rerun our tests, and … wait, they should fail, we haven’t declared $*ANT-MAN anywhere! Well, just like in quantum physics, $*ANT-MAN doesn’t have enough energy to tunnel over the quantum barrier because we haven’t defined him yet.

So let’s do that, but remember that $*ANT-MAN is a quantum variable, so he can tunnel through the quantum barrier of a function scope. In fact, he can tunnel through any number of them. So, let’s define a new version of subtest() that looks and acts like the old one first before we go boldly where no Perl 6 programmer has gone before.

sub Subtest( Str $rule-name, Block $test-code ) {
  subtest $rule-name, $test-code;
}

We should be able now to replace the outer subtest() block with our new Subtest() block, and it should act just as it used to.

Subtest 'binary-number', {
  subtest 'failing', {
    ok fail( '0b', 'binary-number' );
    ...
  };
  ...
};

Tunneling through

Our test suite still works, and the output still is what we expect. Now, let’s give $*ANT-MAN enough energy to tunnel through the quantum barrier by defining him as the subtest name we want:

sub Subtest( Str $rule-name Block $test-code ) {
  my $*RULE-NAME = $rule-name;
  subtest $rule-name, $test-code;
}

And now run our test suite. Which… doesn’t change. Come to think of it, we don’t want it to change. If it did change, we’d have to go through and change all of our test suites, which would be bad. So, putting things together, this code works just fine.

sub fails( Str $sample, Str $rule-name? ) returns Bool {
  !?( $g.parse( $sample, :rule( $*ANT-MAN // $rule-name ) );
}
sub Subtest( Str $rule-name Block $test-code ) {
  my $*RULE-NAME = $rule-name;
  subtest $rule-name, $test-code;
}

Subtest 'binary-number', {
  subtest 'failing', {
    ok fails( '0b', 'binary-number' );
  };
};

Notice by the way that $*ANT-MAN has tunneled through not one but two function signatures to get to where he is. And to prove it, finally, delete the inside ‘binary-number’.

Subtest 'binary-number', {
  subtest 'failing', {
    ok fails( '0b' );
  };
};

So ‘binary-number’ gets passed along to $*ANT-MAN who jumps into the quantum realm, tunnels through the outer and inner pairs of braces, and finally lands in fails() where he passes the value on to the :rule() declaration. Whew, that’s a lot of work.

Oh, snap.

(sorry, couldn’t resist.) If you’re like me, and I know I am, you’ve probably come across a few cases where this technique would come in handy. Especially if you’re dealing with legacy code. Sometimes you need to add just one little flag to a function and set that flag in a top-level handler on one page of a website.

The catch is that between the lower level and the top level there’s a chain of 8 function calls where you have to add that as a parameter. Wouldn’t it be nice if there was a workaround? Well, in Perl 6 there is.

Thanks for getting all the way to the bottom of this, my inaugural article on Perl 6 on the next generation of the Perl Fisher website. Feel free to leave comments and constructive criticism in the comments section below, and come back every so often to watch the website grow over the coming months.