Day 20 – Bridging the gap

Perl 6 arrived last year, after quite a time, I might say! It promised a number of things, which we awaited eagerly. Some it delivered (a solid and sane threading model, for example), some it delivered in perhaps unexpected ways (it was ready by Christmas, after all), but some of them didn’t end up quite how we expected them to. One of those things is Perl 5 interoperability.

It should come as no surprise that CPAN, our legendary repository of modules for Perl 5, with readymade, well documented and thoroughly tested solutions for just about anything you can imagine[1] is quite unlikely to get ported to a completely new language. The bold move to drop backwards compatibility between Perl 5 and Perl 6 was necessary, but it came at a price: the pride of our community, the thing we used to call our language with Perl 5 itself being merely a VM to run it, our bread and butter is now doomed as a mere historical artifact, a legacy we may never live up to, but in the brave, new world of Perl 6 hardly useful anymore. What a loss that would be! Good news is that one of the assumptions of Perl 6 was that it’ll be capable of running Perl 5 code alongside Perl 6 code, loading Perl 5 modules and using them from Perl 6, among other things. Entire power of “good, old” CPAN available at your fingertips, dear reader. Bad news is, however, that this particular functionality we have not entirely get.

The original spec…ulation[2] documents said that you should be able to start a Perl 5 block of code in your Perl 6 code and expect it to work, as so:

grammar Shiny-New-Perl6-Things {
    ...
}

{
    use v5;

    sub good-old-perl5-things($$&) { ... }
}

That requires the Perl 6 compiler to be able to parse Perl 5 (yes, yes, I know; thankfully, the speculation also allows for a “well-behaved subset (…) much like PPI” :)) and execute it, providing interoperability between one and the other. Admirable as it is, it turns out to be much more complicated than it looks to be (and if it does look simple to you, remember to hug a Perl 6 core hacker next time you meet one). It doesn’t mean that there is no effort to make it reality (v5 being one of them, check it out!), but it’s not quite the promised land we were… promised. Yet, I hope! But now is now, and work needs to be done. So what are we left with?

Well, one of the things Perl 6 did deliver marvelously is the foreign function interface, most often used as a way to call C code from Perl 6. So some of us sat down and thought: well, Perl 5 is embeddable and is available to be ran as a C library, so to say. Perl 6 can call functions from C, C can execute Perl 5 code, what’s really stopping us from putting the two and two together? Quite a bit of hard work, but the goal is worthy, and smart, hardworking people is something we have quite in handy in our community. And so, Inline::Perl5 was born.

~~~

The simplest thing are hardly the most exciting ones, but let’s start somewhere:

use Inline::Perl5; # I will be skipping this from now on

my $p5 = Inline::Perl5.new;
$p5.run('print "Hello, older sister!\n"');

Or in a more Perl6-y way (and shorter, so better, right?):

EVAL 'print "Hello, older sister!\n"', :lang<Perl5>;

“But tadzik, how is that better than shelling out and having the external Perl 5 process do some predetermined thing?” Ah, it is better though: we can already drag the results out of the Perl 5 land and get it as a proper, Perl 6 object:

use Inline::Perl5;

my %thing = EVAL 'my %stuff = split(/[=,]/, "foo=bar,baz=beta"); \%stuff', :lang<Perl5>;
for %thing.kv -> $k, $v {
    say "$k => $v"
}

When a little elf saw this, it shouted “woo! Does that mean I can stuff whatever complex magic I want in that EVAL block and I’ll get back a working Perl 6 object?” Why yes, little elf. You can load modules, create objects and pass them around between one Perl and the other, and they’ll work just as you think they will. Check this hot stuff out:

my $mech = EVAL ' use WWW::Mechanize; WWW::Mechanize->new()', :lang<Perl5>;
say $mech.WHAT; # Perl5Object
$mech.get("https://perl6.org");

“Holy Moly!” the little elf could hardly contain itself. This means that the dream of having CPAN still available is still there, still reachable, and as usable as always! Can things get any better?

Hah! Why, I’m glad you asked. Remember how we made EVAL, the Perl 6 mechanism work in the Perl 5-augmented way and produce meaningful results? Syntactic magic is not a Perl 5 exclusive thing, you know. What makes you think we can’t teach use and alike to cooperate with Inline::Perl5 as well?

“Wait, you don’t mean…”

Oh yes, little elf. Yes I do:

use WWW::Mechanize:from<Perl5>;

my $mech = WWW::Mechanize.new;
$mech.get("https://perl6.org");

You don’t always see an elf cry, but when you do, it’s the tears of joy. Just like the ones I saw now, reflected in the comforting, blue-ish[3] light of the computer screen. It’s all there, integrated almost seamlessly, just as the speculations promised. The only price you pay is one use statement and some annotations on the ones that come from The World That Used To Be, and the rest works perfectly fine. All the power of CPAN in all the delicious wrapping paper of the new, shiny Christmas gift of a language. Little elf is a practical little being though, so it immediately started looking for edge cases that’ll prevent its real-world code from being utilized in this way. “I can create objects from Perl 6, call methods from Perl 6, get results back… as long as it can be represented with an object and method calls there’s is no limit to what I can do; in the worst case I can always wrap the good old Perl 5 module in something that plays nice with our limitations…”

I raise my eyebrow, Spock-like. “When would you need to do that?”

“Oh, you know”, started the little elf, “when an exception gets thrown for example.”

We almost couldn’t believe our ears when we heard Santa himself mutter “LOL” from behind his screen. We looked in confusion as he sent us this snippet:

my $mech = WWW::Mechanize.new;
try {
    $mech.get("xmas://perl6.org");
    CATCH {
        default {
            say $_.perl;
        }
    }
}
# Output: X::AdHoc.new(payload => "Error GETing xmas://perl6.org: Protocol scheme 'xmas' is not supported at -e line 0.\n")

The little elf looked almost indignant. “Oh for dancing reindeers, is there anything this thing can’t do!?”

“Like what?” Rudolph asked casually, passing by on its four legs.

“Gee, I don’t think I… ah!” it exclaimed suddenly, finding in its memory a particularly complicated piece of code running the facility at this very moment. “I guess if I need to create objects that are subclasses of existing Perl 5 classes I’m a bit out of luck, am I not?”

“What makes you think that wouldn’t just work as you expect it to?”

“I… heh, I guess I should’ve tried it first. So all the complex things, DBIx::Class, Catalyst, they can all work with this just fine?”

“Been there, done that” muttered Santa from behind his screen again, while the servers hummed peacefully running the now part Perl 5, part Perl 6 production code in the gift factory.

“Well, the future sure does seem bright, does it not?”

“Yes it does”, I replied. “It really does.”

Light years away, a star shone bright, and while it’ll take us a few more days to actually see it above our heads, it is already there, sooner than we thought it would be.

~~~

We may not have gotten the seamless interop we asked for, but for all practical purposes our marvelous Perl 5 legacy is far from gone. It may be almost surprising how much you can already get done with it. Those few battle-tested modules that need glueing together? This nasty script that needs refactoring so badly it may as well end up being a full-blown rewrite? That little project that you dreamed of using Perl 6 for, but never expected to have all the dependencies available (and good, and fast, and proved to work)? It’s time to try it. Your star may already be shining.

[1] I swear I remember seeing a module that existed for the sole purpose of uploading the My Little Pony fanfiction to an appropriate place. If you can help me find it, there’s a prize, why the heck not!

[2] We try very hard to not call it specification :)

[3] Yes, we know about Redshift in the Santa Claus Magical factory, but it’s December, crunch time, we can’t afford to go to bed quite at the time we wish we would be able to. Compromises had to be made.

7 thoughts on “Day 20 – Bridging the gap

      1. Excellent, thank you! :D

        Happy Hearth’s Warming to you and yours — merry Christmas!

    1. It is doable, though it’s a bit quirky to get right and you have to give up some syntactic sugar to make it work. Long story short: since Perl 5 interpreter is not thread-safe by itself, you need a new instance of it for each Perl 6 thread. Similarly, module loading needs to be done per-Perl5. So in each Perl 6 thread you’d typically do something like:

      my $p5 = Inline::Perl5.new;
      $p5.use(‘My::Module’);
      my $obj = $p5.invoke(‘My::Module, ‘new’);

      And use $obj normally. Probably deserves its own small blog post though :)

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.