Thursday, September 15, 2016

The not so killer features of Perl 6

Perl 6 doesn't really lack for killer features. If you ask the typical user what they love about it, they'll cite grammars, metaoperators, slangs and many other game-changing features. But we rarely have to write code that just happens to take advantage of all of the best features of a language. So what is it that makes Perl 6 a pleasure to just... use?

Here are a few of those features that I find myself using casually.

Trivial class construction

Here's a class:

class Server {
    has $.port;
    has @.active;
    has $!socket;

    method listen { ($!socket = IO::SomeService.new(self.port)).listen }
    method connect {
        my $connection = $!socket.connect;
        self.active.push: $connection => start { self.service: $connection };
    }
}

Now, you might look at this and think, "that's all well and good, but it's an example." This is true. There's no code for servicing the asynchronous Promise initiated by the "start" command, nor is there a definition of what this  "SomeService" is, which presumably is doing the interesting work.

But here's what you can write immediately, without any other infrastructure:

    my $server = Server.new(:port(2000));

Where's the constructor that knows how to take a "port" parameter? Built for you. Where are the accessor functions, used in the class, "port" and "active"? Built for you.

All you have to do in writing a Perl 6 class is write the code that does what you want. If you have some special needs in a constructor, sure you can get under the hood (boy can you get under the hood!) but you don't have to.

As a small side-feature, notice that I don't declare parameter lists for my methods, above. The invocant is always implied but unnamed, accessible through the meta-variable "self". To be explicit, I probably should define an empty parameter list (which actually has different semantics when someone tries to pass a parameter). But either way, I never have to waste my time writing out "self" on every method definition.

List semi-flattening

Something that I find myself doing all the time in other languages that feels wrong is constructing a list out of single items in order to be able to do list concatenation with them. Here's an example from Python:

    list2 = ['apple'] + list1

In Perl6, we can flatten just the first layer of an array into a list expression:

    @list2 = 'apple', |@list1;

This also happens to work well with any sort of datastructure that can be flattened to a list, so you don't have to worry about the type of a thing, just what it can do.

Context markers

Code gets much easier to read in Perl6 because you can mark an expression as having a specific type context. Here's an example:

    sub binary_digit_of($n, $m){ ~$n.base(2).substr($m,1) }

Now, you don't need That "~" there. It imposes a string context, and substr only returns strings. So why? Because putting it there clearly tells anyone looking at this statement that it's operating in a string context, even if it's calling some obscure method that might return an integer on Tuesdays, this expression is going to give a string.

Sure, you could call .Str on the result and get the same effect, but it's not right up there in front, telling you the context at first glance, it's just another method call in the chain. This is optional, of course, but it's nice to have that tool handy when there might be ambiguity or you want to enforce your assumptions.

Note: in this example, we could have just declared a return type for the function. The context imposed by "~" or "+" or "?" works on any expression, but there are definitely alternatives in specific situations.

Named variable passing

One of the features that I find myself using all the time and absolutely loving is the ability to pass a variable to a function as a named parameter, using the variable name as the parameter name. Here's an example:

    sub firstline($file, $enc) { open($file, :r, :$enc).get }

Normally, you would pass a named parameter with the ":param(value)" syntax, but when the "param" name is the same as the name of the variable, you can abbreviate to this shorter ":$variable" syntax. This makes quite a lot of named parameter passing much simpler. Where, in other languages, you often find yourself writing "name=name, size=size, duration=duration..." Perl 6 lets you avoid all of the redundancy.