TITLE
Run Perl 6 Today!
BLURB
For a while, you could have thought Perl 6 was vaporware. It isn't. You can install Pugs and run Perl 6 programs in five minutes.
(This isn't a module; It's an article to appear in the December 2005 issue of The Perl Review.)
BODY
Perl 6 is an incredibly fun language to play with. It isn't finished yet, and in fact for a while it looked like it would take forever to arrive. But thanks to the Pugs project, you can try it out right now and start hacking Perl 6 straight away.
First things first
[[ This section could be placed in a sidebar. If so, the previous paragraph should point to it. ]]
If you're on *nix, one of these commands should get you up to speed:
apt-get install pugs pugs-doc # Debian
pkg_add pugs # FreeBSD
emerge pugs # Gentoo
On Windows, you can fetch the PxPerl binary distribution from this site:
http://www.pxperl.com/?pxperl
If your OS isn't one of the above, or if you want to build Pugs yourself, you'll need the latest GHC compiler from http://haskell.org/ghc/, and then install Perl6::Pugs via CPAN, like a standard Perl 5 module. Compiling Pugs takes a lot of memory, so try this on a strong machine.
What's in the box?
You get pugs, the experimental compiler for Perl 6. Along with it come plenty of short example programs, some modules, and documentation. pugs can run programs from files and via the -e switch, just like perl, but it also has an interactive mode which is great for quick things. In Perl 5, you'd probably run perl -de 42
for this; here, just run it with no parameters. You'll get a welcome banner and a prompt.
[[ The screenshot is optional :-) ]]
$ pugs
______
/\ __ \
\ \ \/\ \ __ __ ______ ______ (P)erl6
\ \ __//\ \/\ \/\ __ \/\ ___\ (U)ser's
\ \ \/ \ \ \_\ \ \ \/\ \ \___ \ (G)olfing
\ \__\ \ \____/\ \____ \/\_____\ (S)ystem
\/__/ \/___/ \/___/\ \/____/
/\____/ Version: 6.2.10
\/___/ Copyright 2005 by Audrey Tang
--------------------------------------------------------------------
Web: http://pugscode.org/ Email: perl6-compiler@perl.org
Welcome to Pugs -- Perl6 User's Golfing System
Type :h for help.
Loading Prelude... done.
pugs>
We are now ready to run our canonical first Perl 6 program:
pugs> say "Hello, world!"
Hello, world!
Bool::True
pugs>
The say
builtin works just like print
, but puts a newline at the end. You'll find there are many small improvements of this sort. Since we are in an interactive environment, we also got the result of the whole expression we'd evaluated; in this case the print was successful, so it returned a true boolean value. You can also run this program from the command line. Would it be Perl if you couldn't?
$ pugs -e 'say "Hello, world!"'
Hello, world!
$
If you like OOP, you'd be pleased to see that this works as well:
"Hello, world!".say;
[[ Not sure the following bit isn't too much of a digression at this point. I'd like to make the point about lists being candidates for method invocation but maybe arrange this to come later. ]]
As does this:
"Hello, world!".split("").join("").say;
Everything can be used like an object in Perl 6. In the last example, the string is getting the split
method invoked on it; the result, a list, responds to the join
method and returns a string, which is printed by say
. Of course, you could spell this in longhand the same way as you could in Perl 5:
my $original = "Hello, world!";
my @characters = split "", $original;
my $rejoined = join "", @characters;
print $rejoined, "\n";
[[ (EM) this seems overstating it to me... the real P5 equivalent would be:
print join("",split //,"Hello, world!"),"\n";
]]
[[ (GY) sure. This section needs work; I was steering this towards the point below that this isn't "just" a chain of builins, it's a chain of builins dispatched as methods. Leaving for now until I word it better. ]]
# This code is valid Perl 5 and Perl 6. You will often
# find Perl 5 code works as is, or with little modification,
# in Perl 6.)
But aggregates can be treated like objects too, so @characters.join("")
works in Perl 6 where @characters->join("")
didn't in Perl 5. (Yes, ->
is uniformly a dot now.)
[[ End digression. ]]
Who's this new dog?
[[ XXX: WRITEME ]]
A Catalog of Cool
Perl 6 offers many cleanups and rationalizations over Perl 5, as well as some shiny new features. If you've been programming Perl 5 for a while you may have gotten used to some quirks of the language, so much that you don't see what the fuss is about and why you'd like to learn Perl 6. I'll list just a few things, and limit myself to what Pugs already supports.
- Function signatures
-
Up until now, subs in Perl didn't have much by way of formal parameter specification. Sure, this made them very flexible (take a
@_
and do whatever you like with it), but it also meant that for fairly straightforward things like argument validation or default values you had to write code yourself, or rely on on an external module.Take the following (Perl 5) code:
sub make_car { my $model = shift || die "no model"; my $color = shift || "black"; my $doors = shift || 4; # ...
Expressing parameters is flexible, yes, but it's also tedious. Even where there are no default values there can be trouble: for example, if you decide you don't like multiple calls to
shift
and consistently code in this style:sub my_func { my($foo, $bar, $baz) = @_; # ...
Beginners (and insufficiently caffeinated late-night hackers) can easily make one of the following disastrous mistakes:
my ($foo, $bar, $baz); # oops, forgot " = @_" my ($foo, $bar, $baz) = shift; # oops, used shift() instead of "@_" my $single_arg = @_; # oops, will usually be 1
The Perl 6 compiler has syntax to save you from all this. Here's how you spell out the car example:
sub make_car ($model, $color? = "black", Int $doors? = 4) { ... }
This single line does more than the four Perl 5 lines did. First, it specifies that the function receives three parameters, one of which is mandatory. The
?
in$black?
and$doors?
means they are optional; in both cases default values are supplied. You don't have to give one: the default default is undef. On the other hand, if you do give a default, the?
itself becomes optional.The
Int
mark on the last parameter means calling the function with a non-integer will raise an error. As you can see, this is an optional feature: you don't have to declare types everywhere.How is this
make_car
used? These are obvious:[[ (EM) Maybe source code examples should adhere to PBP ?? ]]
make_car("VW Beetle", "green", 2); make_car("Ford Model T"); # black, 4-doored make_car(); # error: model not supplied make_car("Fiat", "Red", 3, "sunroof"); # error: too many args
But there's more. Ever used a library that has functions with many parameters? It can get kind of hard to keep track of their order. This is why many complex libraries use hashes for named args, but coding up validation for that is once again tedious. Perl 6 lets any function be called with named args automatically:
make_car(doors => 2, model => "VW Beetle") $widget.add_accelerator(signal => "clicked", group => $accel_group, key => GDK_n, mods => GDK_MOD1_MASK, flags => GTK_ACCEL_VISIBLE);
You've probably seen Perl 5 functions that go out of their way to accept either positional args, or a hash or hashref with named args. There's no need to do that any more. If you have a function you'll be calling many times with a changing set of arguments, you can keep them in a hash and use the comma reduce operator
[,]
to flatten it:sub make_many_cars ($howmany) { my @cars; for 1 .. $howmany { # the parens can be omitted here in Perl 6 my %car_prefs = get_user_prefs(); push @cars, make_car([,]%car_prefs); } return @cars; }
- Lightweight closure syntax
-
Every block acts like a closure in Perl 6. The syntax is at once simplified -- you don't need to say
sub
to declare anonymous pieces of code -- and extended to include formal parameters.my $beeper = { say "beep!" }; # $beeper is a coderef. No need for "sub". $beeper(); # says "beep!". No perl5ish "->" here. my $square = -> $x { $x * $x }; # use "->" to introduce args my @squares = map $square, 1 .. 10;
Control structures typically take closures, so you can, as in the example below, iterate with
for
with more than one element every time.zip
is a function that takes a few lists and returns interleaves their elements:for zip @Xs, @Ys -> $x, $y { push @differences, $x - $y; }
Of course, this kind of thing is usually better expressed with map, which can also take more than one element at a time.
# Infix "¥" means the same thing as "zip"; this is a visual analogy. # See the sidebar on Unicode operators for details. my @differences = map -> $x, $y { $x - $y }, @Xs ¥ @Ys;
There's even a feature for the extremely lazy: formal parameters with no predeclaration. Variables mentioned in a closure that have the
^
secondary sigil (called "twigil" in Perl 6 speak) are all inferred as parameters to the block.my @differences = map { $^x - $^y }, @Xs ¥ @Ys;
How does this compare with Perl 5? If
@Xs
and@Ys
can be assumed to be the same length, then this isn't too bad, though the explicit subscripting does make it less elegant:my @differences = map { $Xs[$_] - $Ys[$_] } 0 .. $#Xs;
If we want to allow for lists of different length, though, we need a solution that's much more involved:
sub map2 (&@) { my ($code, @list) = @_; my @output; while (@list) { my($x, $y) = splice @list, 0, 2; push @output, $code->($x, $y); } return @output; } sub zip2 (\@\@) { my($Xs, $Ys) = @_; my @output; for my $i (0 .. max(scalar(@$Xs), scalar(@$Ys))) { push @output, ($i < scalar(@$Xs) ? $Xs->[$i] : undef), ($i < scalar(@$Ys) ? $Ys->[$i] : undef); } return @output; } sub max { $_[0] > $_[1] ? $_[0] : $_[1] } my @differences = map2 { $_[0] - $_[1] } zip2(@Xs, @Ys);
This took care of throwing undef instead of missing values from the shorter list, while not growing the original. It even uses some fancy prototypes to avoid gratitious syntax (the
sub
keyword and reference-taking backslashes). It is much longer than the Perl 6 version, but also less efficient (two temporary copies of the whole data are created in memory -- the Perl 6 version never allocates even one extra copy). Since we used prototypes, the utility functions need to be declared above the actual code (or stowed in a module, of course). But the strongest point against this is how it isn't extensible; if you wanted to take the differences of elements in three lists, you'd need to write more support code. In Perl 6, you just saymy @differences = map { $^x - $^y - $^z }, @Xs ¥ @Ys ¥ @Zs;
- Many (but systematically arranged) operators
-
If non-ASCII operators and parameters that don't need to be declared haven't blown your mind yet, good. The two-list difference operation from the last section could also be written like this:[1]
my @differences = @Xs »-« @Ys;
»«
is the hyper metaoperator that take a regular operator and lift it to work on lists. You can still use them when you don't have two lists:my @halves = @original »/« 2; # 2 is upgraded to "(2, 2, 2, ...)"
Relatedly, there's a way to reduce long expressions, such as sums or products, with the
[]
metaoperator:my $sum = [+] (1, 2, 3, 4); # 1 + 2 + 3 + 4 my $prod = [*] (1, 2, 3, 4); # 1 * 2 * 3 * 4
If these sound a little too abstract, rest assured that they do become quite handy after you get used to them. Let's introduce two nice and less funky improvements. The first is a new syntax for quoting words, similar to Perl 5's
qw
:my @suspects = <Hockney McManus Fenster Keaton Verbal>;
Like
qw
in Perl 5,<...>
single-quotes its arguments. If you want interpolation, you can use the«...»
variant.The second convenient new operator is
//
, the defined-or operator. You'd typically use it like this:my $output_file = lookup("output_file") // $DEFAULT_FILENAME;
In Perl 5, you might use
||
but risk the gotcha of the config value being set to a file called "0
". You'd have to spell this the long way:my $output_file = defined lookup("output_file") ? lookup("output_file") : $DEFAULT_FILENAME;
This becomes especially annoying when you want to check in more than one place (for example, what if you can also get the output file from an environment variable?). Which brings me to the point of the digression, which was to suggest that if you had a function to
interrogate
a suspect, you could talk to them one by one until you had an answer:my $answer = [//] map &interrogate, <Hockney McManus Fenster Keaton Verbal>;
It's important to note that unlike Perl 5, map is lazy, which means it only "eats up" one element of input as an element of output is required. In this case, Verbal may not feel the heat at all if (say) McManus talks first.[2]
Another developement that's taken place is the normalization of bitwise operators. Where you once had
$x | $y
mean different things depending on whether the two variables were numbers or string -- with the kludgy(0+$x) | (0+$y)
workaround guard to force numeric interpretation -- the operators now explicitly state what kind of context they impose. This means that the amount of operators is large, but it is very clear what they do. The idea is that these operators now take a mandatory prefix:+
,~
, or?
; they stand for numeric, string, or boolean context.# Perl 5 print 7 & 54; # 6 print "7" & "54"; # "5" # Perl 6 print 7 +& 54; # 6 print "7" +& "54"; # 6 print 7 ~& 54; # "5" print "7" ~& "54"; # "5"
[[ more: mention
.
again, and~
. We don't want to turn into S03 here but these things are all pretty prominent when someone sees p6 code for the first time. ]] - Junctions
-
Now that
|
,&
, and^
no longer mean bitwise operations, they are free to be used for something else: junctions. These are several values lumped up into one. They are useful in clarifying logic expressions:my $options = '--help' | '-d' | '-e'; if all($x, $y, $z) eq $options { say "All options are valid!"; }
[[ XXX: this example is stolen from Autrijus's Apocalypse Now talk. Maybe think of something else? ]]
- Cleaned-up OOP
-
When version 5 introduced object-oriented programming to Perl, it did so with a minimal change to the existing syntax. It also mandated very little about how objects are arranged: the vast majority of Perl 5 classes pick blessed hashrefs for their all-around usefulness, but the object system itself is flexible and can use other things, such as arrays or even glob references. Certainly this has afforded authors with great creativity and power, but it has also made learning Perl OOP a little more difficult than it needed to be. If you do use the de-facto standard of hash elements for object fields, you are at risk of falling for some of the most frustrating bugs, the kind you don't feel you'd learned anything from after you'd spent hours hunting them down:
$self->{colour} = "red"; print $self->{color}; # oops!
By rights, this should be something the compiler catches, and in Perl 6, it is. Classes are more than "just" packages. Instance variables are declared with the new
has
keyword:class Car; has $.model; has $.color; has Int $.doors;
For this money, you get a constructor with basic initialization, and three automatic accessors that can act as lvalues.
my $wheels = Car.new(model => "Fast 'un"); $wheels.doors = 2; $wheels.color = "red";
If you want, you can provide your own
BUILD
method that has the class-specific smarts. For example, it may give a default color, or limit cars to a certain number of doors.The
.
twigil meant the members above were public, and so they had accessors created for them which are visible outside the class. You can also declare private members:has Int $!odometer; # note "!" twigil, meaning "private". has Location $!loc; # let's assume there is a Location class. # ... $wheels.odometer -= 100; # nice try, but it won't work.
Methods are now declared with a keyword of their own. This distinguishes them from regular
sub
s, and also means that you don't have to declare the invocant (by convention often called$self
or$this
in Perl 5) yourself.method beep { say "beep!" } method drive (Location $destination) { $!odometer += $!loc.distance($destination); $!loc = $destination; self.beep; }
There is, of course, much more to OOP in Perl, but there's no room to discuss it here. We'll just mention the idea of Roles, where a class declares what kind of things it does. Part of the implementation of a class may be taken directly from the providing Role. It works differently from inheritance and is worth looking into.
SIDEBAR: Confused by Unicode?
Perl 6 defines a few operators that aren't ASCII, though they all have ASCII fallbacks. Here's the current (short) core list:
Op Fallback Meaning
«...» <<...>> Interpolating word quotes
¥ Y or &zip Zip lists together
Since it's easy to add new operators, expect mathematical modules to say things like ⌈$PI⌉ to mean 4.
[[ Those are Unicode LEFT CEILING and RIGHT CEILING, U+2308 and U+2309. They look just like square brackets with the lower serifs sawn off. Sorry about the pain it must be to include this, it's worth mentioning since this kind of thing is definitely going to show up in Perl 6 code. ]]
The file docs/quickref/unicode
in the Pugs distribution explains how to type Unicode in popular editors.
The examples/
directory
When you download Pugs, you get plenty of working example code to play with. Let's look at a few of the things there.
examples/network/http-client.p6
#!/usr/bin/pugs
use v6;
say "*** Fetching from the Jerk It (tm) RSS feed...";
my $hdl = connect("www.phreeow.net", 80);
$hdl.say(
"GET /cgi-bin/jerkme.rss HTTP/1.0\n",
"Host: www.phreeow.net\n"
);
$hdl.flush;
if ($hdl.slurp ~~ rx:perl5{<description>(.+)</description>\s*</item>}) {
say $0;
}
else {
say "*** Oops, connection failed."
}
This example is one of the oldest, added two months after the Pugs project started. It implements a very simple web client. The code is fairly straightforward, but it illustrates a few features of Perl 6 and of Pugs. First, you'll notice that connect
returns an object that's used in a totally OOPish way. At the time this was added, networking wasn't fully specced: expect this to be called something like Socket.connect
in the official Perl 6 specification, though there may very well be an IO::All
-ish way to create lazily defined IO objects as well. Pugs just went away and implemented a reasonable approximation.
The client speaks very simple HTTP, .flush
es the request to make sure it's sent over the wire, and analyzes the response. This line is worth breaking down:
if ($hdl.slurp ~~ rx:perl5{<description>(.+)</description>\s*</item>}) { ... }
# Perl 5 equivalent
sub slurp { local $/; readline shift } # works on open filehandles only
my $response = slurp($hdl);
if ($response =~ m{<description>(.+)</description>\s*</item>}x) { ... }
The basic idea is clear -- get the whole HTTP response, perform a capturing pattern match. There are a few important details though. First, both P5 and P6 versions of the code use Perl 5 regular expressions for the match. Perl 6 defines a new language called Rules, but an implementation wasn't available when this code was written, so it used a Perl 5 regex instead. As you can see, these are accessible through rx:perl5
, which is the rx
Rule keyword with the perl5
adverbial modifier attached to it.
[[ XXX: not sure if this is worth the break in pace--
You omit the modifier if you want a P6 rule; and just like you can omit m
in Perl 5 if you just want to /match something/
, you could do the same if you were using /
as your delimiters. In full Perl 6, this line becomes:
..and then port the regex to a rule, which kinda shows XML is annoying
to pattern match quickly and dirtily. So maybe skip this.
]]
Regardless of which pattern engine is used, the capture, if successful, goes to $0
and not $1
as it would in Perl 5. The other difference is that instead of using =~
, we now use ~~
to match. The change is not just one of spelling: ~~
is the shiny new smart match operator, which has very well-defined semantics for matching anything against anything else. That is, you know in advance what it'd do if you say something like %hash ~~ @array
or $dow ~~ (0 .. 7)
.
examples/games/animals.p6
$ ./pugs examples/games/animals.p6
Is it a dog (yes/no)? n
No!? What was it then? cat
And a question that distinguishes a dog from a cat would be? Does it purr?
And for a cat, the answer would be... (yes/no)? y
Play again? (yes/no)? y
Does it purr? (yes/no)? y
Is it a cat (yes/no)? y
I got it!
Play again? (yes/no)? n
Bye!
{('no' => 'dog'), ('question' => 'Does it purr?'), ('yes' => 'cat')}
$
The code for this simple guessing game is just a little too long to fit here, so I'm just going to highlight some nice features. The program tries to guess what animal you are thinking of; if it fails it asks for distinctive knowledge it can use next time. Obviously, this knowledge is expressed in some data structure: the nice thing about this example is that the last thing it does when exiting is dump this structure. Or rather, the really nice thing about this is the code to do it:
say $info.perl;
.perl
is similar to Data::Dumper
, but is built into perl. It provides sensible serialization for objects as well. If the Pugs hackers have their way, you can expect an analogous .yaml
method too, although it may not be in the core.
Recall the smart match operator from the previous example, and let's look at this snippet of code:
given $input {
when "y" { 1 }
when "n" { 0 }
default { yes($q) }
}
This is Perl 6's switch statement. given
sets $_
(the topic as it's now called) to be $input
, and then runs the code in the block. Each when
block performs a smart match against the topic -- it's shorthand for something like
if $_ ~~ "y" { 1 } else { ... }
The beauty of this construct is that smart matches work on everything, so you can have code like this:
given $food {
when "My tortilla" { say "hey! give it back!" }
when Pizza | Lazagna { .eat }
when .caloric_value > $doctors_orders { warn "no, no no" } # XXX: make this work
}
More information
http://pugscode.org/ - The Pugs homepage. Contains links to code and binaries, presentations and talks, journals and news, automatic smoke test results.
http://planet.pugscode.org/ - PlanetSix aggregates the development journals of leading Perl6/Pugs personnel, as well as semiweekly summaries of the Perl 6 mailing lists.
http://svn.openfoundry.org/pugs/ - Subversion repository of the pugs code, which you can browse with a web client. Check out the examples/
folder!
The #perl6
channel on irc.freenode.org
- Come hang out with the hackers! This channel seldom sleeps as developers come from all around the world.
http://dev.perl.org/perl6/doc/synopsis.html - The Perl 6 Synopses are summaries of the longer Apocalypse and Exegesis documents, and are more up-to-date. In fact, they function as a specification for Perl 6 implementations, Pugs included. They are, like Pugs, a work in progress.
FOOTNOTES
[1] Yes, @Xs »-« @Ys »-« @Zs
also works.
[2] //
is scheduled for inclusion in perl 5.10. For those interested, patches are available for existing version all the way back to 5.6.
AUTHOR INFORMATION
This article was written by Gaal Yahas <gaal@forum2.org> with the help and inspiration of #perl6. It is distributed under the Creative Commons Attribution-ShareAlike 2.0 license http://creativecommons.org/licenses/by-sa/2.0/.
Thanks to Eric Hodges and Elizabeth Mattijsen for material suggestions.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 257:
Non-ASCII character seen before =encoding in '"¥"'. Assuming UTF-8