Things to be Excited About in Perl 6

October 11, 2015

Perl's creator, Larry Wall, recently confirmed that Perl 6 will in fact be released this Christmas. It's been a long (long) time coming, so the release of Perl 6 has turned into a bit of a joke, especially given the unpopularity of Perl 5 in the past decade. Perl was first made to be a general-purpose scripting language with a focus on quick text processing needs, but ended up growing into the go-to CGI language when the internet became a thing. Not being designed for the way the internet evolved, other scripting languages made to fit that niche better gained popularity over Perl. But Perl 6 is different. Perl 6 is a major departure from Perl 5, completely rethought while still remaining Perly. It isn't made to be the language to replace Ruby or Python on webservers and doesn't aim to be adopted immediately by companies everywhere; rather, it's made to be powerful and fast to write and to just be a joy to work with.

So here are some of the most interesting language features that add up to make Perl 6 worth getting excited about!

Variables

Debugging

Perl 6 comes with some fancy new debugging methods that can be called on variables to print a snapshot of them. There's .gistto give you the gist of the variable's contents, and then there's .perl to give you a string of Perl code that you would run to create an equivalent variable.

say {a => 1, b => 2}.gist; # a => 1, b => 2
say {a => 1, b => 2}.perl; # {:a(1), :b(2)}

You can compare variables at the same time using a neat feature called junctive autothreading. It's a much more readable way of checking a variable against a group of values without using a long chain of ands and ors. Each variable only knows that it's being compared to one thing at a time, though, so you can use this feature with anything without worrying about compatibility. As the "autothreading" in the name suggests, Perl will even try to run comparisons at the same time.

# Equivalent to if $var == 3 || var == 5 ...
if $var == any(3, 5, 7) {
  # ...
}

if $var == 3|5|7 {
  # ...
}

Lists

Perl 5 had some arguably unintuitive design decisions involving scalar and list context, references, and when to use $ and @ sigils. In Perl 6, basically everything is a reference, so it is fine in most cases to use $ variables. This means that the variable is treated as a singular object in most cases, and list context is used in iterations.

my $arr = [1, 2, 3];
for $arr.list -> $i { say $i }; # Use list context to loop

Using list contexts, we can switch variables without using a temporary one.

($x, $y) .= reverse;

Numeric lists are smart and can be declared very expressively.

my $arithmetic = 2, 4, ... 12;
my $geometric = 1, 2, 4, ... 2**32;

Lists can be made lazily now, allowing for some cool new ways to work with infinite series.

my $infinite = 1..*;
my $fibonacci = 1, 1, *+* ... *;
for 3, 6, 9 ... * -> $i { say "Counting infinitely by 3s, we're at $i" }

Lists also come with some pretty convenient methods built in.

my $combos = ("a", "b", "c").combinations;
# ((), ("a",), ("b",), ("c",), ("a", "b"), ("a", "c"), ("b", "c"), ("a", "b", "c"))

my $perms = say ("a", "b", "c").permutations;
# (("a", "b", "c"), ("a", "c", "b"), ("b", "a", "c"), ("b", "c", "a"), ("c", "a", "b"), ("c", "b", "a")) 

Using the cross product operator on a list, you can easily initialize a hash with default values for a given set of keys.

my $hash = ("a", "b", "c") X=> 1;
# a => 1 b => 1 c => 1

You can use .reduce on a list when you have a lot to do in the block passed in, but for simple reductions, Perl 6 has a reduce operator to do it more concisely.

my $sum = [+] 1, 2, 3;

Functions/Subroutines

In Perl 6, although it is a dynamically typed language, you can easily add type checking to any method. If you try to call a function with the wrong argument type, it will fail with an error message at compile time, before you even run your program.

sub say-hi-to(Str $name) {
  say "Hello, $name"
}
say-hi-to("world"); # works
say-hi-to(5); # fails at compile time

In languages where everything is a reference internally, concepts of "passing by value" and "passing by reference" lose meaning. Really, you tend to mean "can the arguments be mutated?" Perl 6 answers this with ro and rw.

sub add-one(Int $x is ro) { $x + 1 } # returns a new copy of x
sub add-one-mutate(Int $x is rw) { $x++ } # mutates x

Functions can specify their domain, so you can have multiple implementations of the same functions that only operate on their specified domain.

multi is-big(Int $n where * > 50) { True }
multi is-big(Int $ where 10..50) { False }

If you're writing a command line program, Perl lets you define separate MAIN functions that automatically get called when passing in the function's name in the terminal. If you don't specify anything, Perl will even show you the function names and signatures of the options available to you.

multi MAIN('add', $key, $value, Bool :$replace) {
  # ...
}
multi MAIN('remove', $key) {
  # ...
}
multi MAIN('import', File, Str :$as) {
  # ...
}

Grammars and Regular Expressions

One of the largest problems people have with Perl 5 is the unreadable syntax of Regular Expressions. Because they're so powerful, they get used often in Perl programs as well as in the numerous other languages that provide implementations of Perl-compatible Regular Expressions. Perl 6 aims to make regex use guilt-free again by introducing a completely new version of its Regular Expression syntax to both add even more features and also make everything more maintainable.

Regexes

By default, whitespace and comments in your regex literal are ignored. That means you can spread your regular expression tokens over multiple lines and add comments explaining what it's doing. Literal strings to be matched are put in quotes.

my regex categorized-url {
  ^^
  '/' <word>     # category is a `word`, where `word` is another regex
  ['/' <page> ]? # optional page in category
  $$
}

So you declare regexes like subroutines with regex, but you can also declare them with some different names to use different mode modifiers.

my token word { \w+ }
# Equivalent to regex { :r \w+ }
# Only parses forward, never goes back to retry if an inner match fails

my rule two-words { \w+ \w+ }
# Equivalent to regex { :r :s \w+ \w+ }
# Only parses forward, and also makes whitespace significant (as if it's a \s)

There are some useful new operators available to you:

rule object { '{' ~ '}' <pairlist> }
# Use the nested subrule operator ~ to parse the end part of a regex and then
# check that it's surrounded by the given pairs (in this case, a <pairlist>
# surrounded by curly braces)

regex word-list { \w+ % ',' }
# Use the modified quantifier % to separate lists of the previous token
# with the next token

Grammars

The largest change is the introduction of grammars. Rather than creating spaghetti code with many unorganized regexes (or, even worse, cramming everything into one massive monolithic regex), you structure your regexes into tokens and combinations of tokens. Then, you can simply match your grammar against a string, and it will get parsed into an abstract syntax tree for you.

Here's a good example from the Perl 6 Grammar documentation demonstrating how a grammar is defined, linked to actions, and used to parse an AST.

grammar KeyValuePairs {
  token TOP {
    [<pair> \n+]*
  }
  token ws { \h* }

  rule pair {
    <key=.identifier> '=' <value=.identifier>
  }
  token identifier {
    \w+
  }
}

class KeyValuePairsActions {
  method identifier($/) { $/.make: ~$/                          }
  method pair      ($/) { $/.make: $<key>.made => $<value>.made }
  method TOP       ($/) { $/.make: $<pair> >> .made                }
}

my $res = KeyValuePairs.parse(q:to/EOI/, :actions(KeyValuePairsActions)).made;
  second=b
  hits=42
  perl=6
  EOI

for @$res -> $p {
  say "Key: $p.key()\tValue: $p.value()";
}

As it turns out, Perl 6 is partially implemented as a Perl 6 grammar. That means you can add syntax into the Perl parser for your program inside your program. This isn't scary, this is an intended core feature of the language made to make Perl even more capable and extensible.

# Define `times` as a language feature
infix:<times>(Int $n, Block $r) {
  for ^$n { $r(); }
}

# Use it immediately
3 times -> { say "hello" };

Object-Oriented Programming

Another problem with Perl 5 was that it didn't contain a good way to do OOP out of the box, although the package Moose is extremely powerful and comes with most distributions of Perl 5. Now, all of those good parts have been merged into Perl 6's default language.

Classes can now extend each other, use mixins (roles in Perl), have public and private methods, and everything else you would expect for object-oriented design. Perl will make you a default constructor out of your public properties so that an object can be initialized by passing in key-value pairs for the public properties you want to assign (and, as always, there is also a way to make your own constructor.) From the Perl 6 OOP documentation (where there are also many more examples of OOP implementations):

role Notable {
  has $.notes is rw;
}

class Journey does Notable {
  has $.origin; # . is public, so it makes a private property and public aliases of the same name to make it accessible
  has $.destination;
  has @!travellers; # ! is private
}

# Create a new instance of the class.
my $vacation = Journey.new(
  origin      => 'Sweden',
  destination => 'Switzerland',
  notes       => 'Pack hiking gear!'
);

# Use an accessor; this outputs Sweden.
say $vacation.origin;

# Use an rw accessor to change the value, mixed in from Notable.
$vacation.notes = 'Pack hiking gear and sunglasses!';

Concurrency

To have a language ready for use on the internet, there needs to be a well-thought-out way to write concurrent code. The traditional way to do this is to use threading, but Perl has decided that there are some better design patterns.

If you're used to Javascript, you'll know about Promises. You can kick off a process and a promise with start, and wait for a list of promises to all be fulfilled with await. This throws an error for you to catch if any promises fail, but as always, Perl provides you with alternate ways to specify how you want to handle failed promises with anyof and allof.

my @quotes = await @currency_exchanges.map(-> $ex {
  start { $ex.get_quote($val) }
});

# A more explicit way of waiting for all of them
my @getting = @currency_exchanges.map(-> $ex {
  start { $ex.get_quote($val) }
});
await Promise.allof(@getting);
my @quotes = @getting.grep(*.status == Kept).map(*.result);

To ensure that code gets executed fast enough (for example, to kill long-running processes), promises can be made that are only true for n seconds before being considered a failure.

my $kept-in-10 = Promise.in(10);

For a producer-consumer concurrency model, you can use channels, a threadsafe queue data structure. Go programmers should feel right at home using these.

my $c = Channel.new;
await (^10).map: {
    start {
        my $r = rand;
        sleep $r;
        $c.send($r);
    }
}
$c.close;
say $c.list;

But why does it matter?

Sure, maybe these features are cool, but you can get by without them without having to learn another language. Why do we need another scripting language, anyway? Don't we have enough? Maybe Perl 6 doesn't offer you enough reason to switch away from anything else, so why even bother?

Here's what I say when people ask me that.

We have no reason to stop making new languages. When we stop trying new things, that's when we stagnate and stop innovating. All the things you love about your favourite language are ideas built on the shoulders of previous ideas. Perl is one of the many languages that have influenced modern programming languages, the most notable example of which being Perl-Compatible Regular Expressions. Plenty of other languages added support for them because they were a good idea that Perl introduced. If you think Regular Expressions are unreadable and unmaintainable, then at the very least it could be a good thing to have Perl 6 so that other languages are more inclined to implement the new version of Perl's regexes so you can have your better regexes without having to leave the comfort of your preferred language. The same thing applies to other language features, too. The existance of Perl 6 will make other languages better, even if you decide to never use Perl yourself.

That is why I care a lot about Perl 6's release and you should, too.

If you do want to try out some of Perl 6's new features now, you actually can! Although it is being officially released on Christmas, it is definitely stable enough to try out and start building with. Check out the Perl 6 compiler and get started!