This page attempts to provide a fast-path to the changes in syntax and semantics from Perl 5 to Perl 6. Whatever worked in Perl 5 and must be written differently in Perl 6, should be listed here (whereas many new Perl 6 features and idioms need not).

Hence this should not be mistaken for a beginner tutorial or a promotional overview of Perl 6; it is intended as a technical reference for Perl 6 learners with a strong Perl 5 background and for anyone porting Perl 5 code to Perl 6 (though note that Automated translation might be more convenient).

A note on semantics; when we say "now" in this document, we mostly just mean "now that you are trying out Perl 6." We don't mean to imply that Perl 5 is now suddenly obsolete. Quite the contrary, most of us love Perl 5, and we expect Perl 5 to continue in use for a good many years. Indeed, one of our more important goals has been to make interaction between Perl 5 and Perl 6 run smoothly. However, we do also like the design decisions in Perl 6, which are certainly newer and arguably better integrated than many of the historical design decisions in Perl 5. So many of us do hope that over the next decade or two, Perl 6 will become the more dominant language. If you want to take "now" in that future sense, that's okay too. But we're not at all interested in the either/or thinking that leads to fights.

A number of Perl 5 modules have been ported to Perl 6, trying to maintain the API of these modules as much as possible, as part of the CPAN Butterfly Plan. These can be found at https://modules.perl6.org/t/CPAN5.

Many Perl 5 built-in functions (about a 100 so far) have been ported to Perl 6 with the same semantics. Think about the shift function in Perl 5 having magic shifting from @_ or @ARGV by default, depending on context. These can be found at https://modules.perl6.org/t/Perl5 as separately loadable modules, and in the P5built-ins bundle to get them all at once.

If you've read any Perl 6 code at all, it's immediately obvious that method call syntax now uses a dot instead of an arrow:

$person->name# Perl 5

$person.name# Perl 6

The dot notation is both easier to type and more of an industry standard. But we also wanted to steal the arrow for something else. (Concatenation is now done with the ~ operator, if you were wondering.)

To call a method whose name is not known until runtime:

$object->$methodname(@args); # Perl 5

$object."$methodname"(@args); # Perl 6

If you leave out the quotes, then Perl 6 expects $methodname to contain a Method object, rather than the simple string name of the method. Yes, everything in Perl 6 can be considered an object.

Perl 5 allows a surprising amount of flexibility in the use of whitespace, even with strict mode and warnings turned on:

# unidiomatic but valid Perl 5

say"Hello ".ucfirst ($people

[$i]

->

name)."!"if$greeted[$i]<1;

Perl 6 also endorses programmer freedom and creativity, but balanced syntactic flexibility against its design goal of having a consistent, deterministic, extensible grammar that supports single-pass parsing and helpful error messages, integrates features like custom operators cleanly, and doesn't lead programmers to accidentally misstate their intent. Also, the practice of "code golf" is slightly de-emphasized; Perl 6 is designed to be more concise in concepts than in keystrokes.

As a result, there are various places in the syntax where whitespace is optional in Perl 5, but is either mandatory or forbidden in Perl 6. Many of those restrictions are unlikely to concern much real-life Perl code (e.g., whitespace being disallowed between the sigil and name of a variable), but there are a few that will unfortunately conflict with some Perl hackers' habitual coding styles:

Should this really be a problem for you, then you might want to have a look at the Slang::Tuxic module in the Perl 6 ecosystem: it changes the grammar of Perl 6 in such a way that you can have a space before the opening parenthesis of an argument list.

Space is required immediately after keywords

my($alpha, $beta); # Perl 5, tries to call my() sub in Perl 6

my ($alpha, $beta); # Perl 6

if($a<0) {...}# Perl 5, dies in Perl 6

if ($a<0) {...}# Perl 6

if$a<0{...}# Perl 6, more idiomatic

while($x-->5) {...}# Perl 5, dies in Perl 6

while ($x-->5) {...}# Perl 6

while$x-->5{...}# Perl 6, more idiomatic

No space allowed after a prefix operator, or before a postfix/postcircumfix operator (including array/hash subscripts).

$seen{$_}++; # Perl 5

%seen{$_}++; # Perl 6

Space required before an infix operator if it would conflict with an existing postfix/postcircumfix operator.

$n<1; # Perl 5 (in Perl 6 this would conflict with postcircumfix < >)

$n<1; # Perl 6

However, whitespace is allowed before the period of a method call!

# Perl 5

my@books=$xml

->parse_file($file) # some comment

->findnodes("/library/book");

# Perl 6

my@books=$xml

.parse-file($file) # some comment

.findnodes("/library/book");

However, note that you can use unspace to add whitespace in Perl 6 code in places where it is otherwise not allowed.

In Perl 5, arrays and hashes use changing sigils depending on how they are being accessed. In Perl 6 the sigils are invariant, no matter how the variable is being used - you can think of them as part of the variable's name.

The $ sigil is now always used with "scalar" variables (e.g. $name), and no longer for array indexing and Hash indexing. That is, you can still use $x[1] and $x{"foo"}, but it will act on $x, with no effect on a similarly named @x or %x. Those would now be accessed with @x[1] and %x{"foo"}.

The & sigil is now used consistently (and without the help of a backslash) to refer to the function object of a named subroutine/operator without invoking it, i.e. to use the name as a "noun" instead of a "verb":

my$sub= \&foo; # Perl 5

my$sub=&foo; # Perl 6

callback =>sub{say@_}# Perl 5 - can't pass built-in sub directly

callback =>&say# Perl 6 - & gives "noun" form of any sub

Since Perl 6 does not allow adding/removing symbols in a lexical scope once it has finished compiling, there is no equivalent to Perl 5's undef &foo;, and the closest equivalent to Perl 5's defined &foo would be defined ::('&foo') (which uses the "dynamic symbol lookup" syntax). However, you can declare a mutable named subroutine with my &foo; and then change its meaning at runtime by assigning to &foo.

In Perl 5, the ampersand sigil can additionally be used to call subroutines in special ways with subtly different behavior compared to normal sub calls. In Perl 6 those special forms are no longer available:

&foo(...)for circumventing a function prototype

In Perl 6 there are no prototypes, and it no longer makes a difference whether you, say, pass a literal code block or a variable holding a code object as an argument:

# Perl 5:

first_index{$_>5}@values;

&first_index($coderef, @values); # (disabling the prototype that parses a

# literal block as the first argument)

# Perl 6:

first{$_>5}, @values, :k; # the :k makes first return an index

first$coderef, @values, :k;

&foo;andgoto &foo;for re-using the caller's argument list / replacing the caller in the call stack. Perl 6 can use either callsame for re-dispatching or nextsame and nextwith, which have no exact equivalent in Perl 5.

subfoo{say"before"; &bar; say"after"}# Perl 5

subfoo{say"before"; bar(|@_); say"after"}# Perl 6 - have to be explicit

Index and slice operations on hashes no longer inflect the variable's sigil, and adverbs can be used to control the type of slice. Also, single-word subscripts are no longer magically autoquoted inside the curly braces; instead, the new angle brackets version is available which always autoquotes its contents (using the same rules as the qw// quoting construct):

In Perl 5, references to anonymous arrays and hashes and subs are returned during their creation. References to existing named variables and subs were generated with the \ operator. the "referencing/dereferencing" metaphor does not map cleanly to the actual Perl 6 container system, so we will have to focus on the intent of the reference operators instead of the actual syntax.

my$aref= \@aaa ; # Perl 5

This might be used for passing a reference to a routine, for instance. But in Perl 6, the (single) underlying object is passed (which you could consider to be a sort of pass by reference).

my@array=4,8,15;

{$_[0] =66}(@array); # run the block with @array aliased to $_

say@array; # OUTPUT: «[66 8 15]␤»

The underlying Array object of @array is passed, and its first value modified inside the declared routine.

In Perl 5, the syntax for dereferencing an entire reference is the type-sigil and curly braces, with the reference inside the curly braces. In Perl 6, this concept simply does not apply, since the reference metaphor does not really apply.

In Perl 5, the arrow operator, -> , is used for single access to a composite's reference or to call a sub through its reference. In Perl 6, the dot operator . is always used for object methods, but the rest does not really apply.

# Perl 5

say$arrayref->[7];

say$hashref->{'fire bad'};

say$subref->($foo, $bar);

In relatively recent versions of Perl 5 (5.20 and later), a new feature allows the use of the arrow operator for dereferencing: see Postfix Dereferencing. This can be used to create an array from a scalar. This operation is usually called decont, as in decontainerization, and in Perl 6 methods such as .list and .hash are used:

While the operator has not changed, the rules for what exactly is matched depend on the types of both arguments, and those rules are far from identical in Perl 5 and Perl 6. See ~~ and the smartmatch operator

If you were using => as a convenient shortcut to not have to quote part of a list, or in passing arguments to a sub that expects a flat list of KEY, VALUE, KEY, VALUE, then continuing to use => may break your code. The easiest workaround is to change that fat arrow to a regular comma, and manually add quotes to its left-hand side. Or, you can change the sub's API to slurp a hash. A better long-term solution is to change the sub's API to expect Pairs; however, this requires you to change all sub calls at once.

In Perl 5, "${foo}s" deliminates a variable name from regular text next to it. In Perl 6, simply extend the curly braces to include the sigil too: "{$foo}s". This is in fact a very simple case of interpolating an expression.

Mostly unchanged; parentheses around the conditions are now optional, but if used, must not immediately follow the keyword, or it will be taken as a function call instead. Binding the conditional expression to a variable is also a little different:

if (my$x=dostuff()) {...}# Perl 5

ifdostuff() ->$x{...}# Perl 6

(You can still use the my form in Perl 6, but it will scope to the outer block, not the inner.)

The unless conditional only allows for a single block in Perl 6; it does not allow for an elsif or else clause.

The given-when construct is like a chain of if-elsif-else statements or like the switch-case construct in e.g. C. It has the general structure:

givenEXPR{

whenEXPR{...}

whenEXPR{...}

default{...}

}

In its simplest form, the construct is as follows:

given$value{# assigns $_

when"a match"{# if $_ ~~ "a match"

# do-something();

}

when"another match"{# elsif $_ ~~ "another match"

# do-something-else();

}

default{# else

# do-default-thing();

}

}

This is simple in the sense that a scalar value is matched in the when statements against $_, which was set by the given. More generally, the matches are actually smartmatches on $_ such that lookups using more complex entities such as regexps can be used instead of scalar values.

Mostly unchanged; parentheses around the conditions are now optional, but if used, must not immediately follow the keyword, or it will be taken as a function call instead. Binding the conditional expression to a variable is also a little different:

while (my$x=dostuff()) {...}# Perl 5

whiledostuff() ->$x{...}# Perl 6

(You can still use the my form in Perl 6, but it will scope to the outer block, not the inner.)

Note that reading line-by-line from a filehandle has changed.

In Perl 5, it was done in a while loop using the diamond operator. Using for instead of while was a common bug, because the for causes the whole file to be sucked in at once, swamping the program's memory usage.

In Perl 6, for statement is lazy, so we read line-by-line in a for loop using the .lines method.

Note first this common misunderstanding about the for and foreach keywords: Many programmers think that they distinguish between the C-style three-expression form and the list-iterator form; they do not! In fact, the keywords are interchangeable; the Perl 5 compiler looks for the semicolons within the parentheses to determine which type of loop to parse.

The C-style three-factor form now uses the loop keyword, and is otherwise unchanged. The parentheses are still required.

for ( my$i=1; $i<=10; $i++ ) {...}# Perl 5

loop ( my$i=1; $i<=10; $i++ ) {...}# Perl 6

The loop-iterator form is named for in Perl 6 and foreach is no longer a keyword. The for loop has the following rules:

parentheses are optional;

the iteration variable, if any, has been moved from appearing before the list, to appearing after the list and an added arrow operator;

the iteration variable is now always lexical: my is neither needed nor allowed;

the iteration variable is a read-only alias to the current list element (in Perl 5 it is a read-write alias!). If a read-write alias is required, change the -> before the iteration variable to a <->. When translating from Perl 5, inspect the use of the loop variable to decide if read-write is needed.

formy$car (@cars) {...}# Perl 5; read-write

for@cars->$car{...}# Perl 6; read-only

for@cars<->$car{...}# Perl 6; read-write

If the default topic $_ is being used, it is also read-write.

for (@cars) {...}# Perl 5; $_ is read-write

for@cars{...}# Perl 6; $_ is read-write

for@cars<->$_{...}# Perl 6; $_ is also read-write

It is possible to consume more than one element of the list in each iteration simply specifying more than one variable after the arrow operator:

In Perl 6 regexes, | does LTM, which decides which alternation wins an ambiguous match based off of a set of rules, rather than about which was written first.

The simplest way to deal with this is just to change any | in your Perl 5 regex to a ||.

However, if a regex written with || is inherited or composed into a grammar that uses | either by design or typo, the result may not work as expected. So when the matching process becomes complex, you finally need to have some understanding of both, especially how LTM strategy works. Besides, | may be a better choice for grammar reuse.

These work in a slightly different way; also they only work in the latest versions of Perl 5.

usev5.22;

"þor is mighty" =~/is (?<iswhat>\w+)/n;

say $+{iswhat};

The iswhat within a non-capturing group is used to actually capture what is behind, and up to the end of the group (the )). The capture goes to the %+ hash under the key with the name of the capture. In Perl 6 named captures work this way

"þor is mighty"~~/is \s+$<iswhat>=(\w+)/;

say$<iswhat>;

An actual assignment is made within the regular expression; that's the same syntax used for the variable outside it.

There is currently no direct equivalent of CHECK blocks in Perl 6. The CHECK phaser in Perl 6 has the same semantics as the UNITCHECK block in Perl 5: it gets run whenever the compilation unit in which it occurs has finished parsing. This is considered a much saner semantic than the current semantics of CHECK blocks in Perl 5. But for compatibility reasons, it was impossible to change the semantics of CHECK blocks in Perl 5, so a UNITCHECK block was introduced in 5.10. Consequently, it was decided that the Perl 6 CHECK phaser would follow the saner Perl 5 UNITCHECK semantics.

In Perl 5, these special blocks must have curly braces, which implies a separate scope. In Perl 6 this is not necessary, allowing these special blocks to share their scope with the surrounding lexical scope.

If you put BEGIN and CHECK phasers in a module that is being precompiled, then these phasers will only be executed during precompilation and not when a precompiled module is being loaded. So when porting module code from Perl 5, you may need to change BEGIN and CHECK blocks to INIT blocks to ensure that they're run when loading that module.

The functions which were altered by autodie to throw exceptions on error, now generally return Failures by default. You can test a Failure for definedness / truthiness without any problem. If you use the Failure in any other way, then the Exception that was encapsulated by the Failure will be thrown.

# Perl 5

openmy$i_fh, '<', $input_path; # Fails silently on error

useautodie;

openmy$o_fh, '>', $output_path; # Throws exception on error

# Perl 6

my$i_fh=open$input_path, :r; # Returns Failure on error

my$o_fh=open$output_path, :w; # Returns Failure on error

Because you can check for truthiness without any problem, you can use the result of an open in an if statement:

Int is now arbitrary precision, as is the numerator of Rat (the denominator is limited to 2**64, after which it will automatically upgrade to Num to preserve performance). If you want a Rat with an arbitrary-precision denominator, FatRat is available.

Manipulate where modules are looked up at compile time. The underlying logic is very different from Perl 5, but in the case you are using an equivalent syntax, use lib in Perl 6 works the same as in Perl 5.

In Perl 5, a common idiom for reading the lines of a text file goes something like this:

openmy$fh, "<", "file"ordie"$!";

my@lines=<$fh>; # lines are NOT chomped

close$fh;

In Perl 6, this has been simplified to

my@lines="file".IO.lines; # auto-chomped

Do not be tempted to try slurping in a file and splitting the resulting string on newlines as this will give an array with a trailing empty element, which is one more than you probably expect (it's also more complicated), e.g.:

# initialize the file to read

spurt"test-file", q:to/END/;

first line

second line

third line

END

# read the file

my@lines="test-file".IO.slurp.split(/\n/);

say@lines.elems; #-> 4

If for some reason you do want to slurp the file first, then you can call the lines method on the result of slurp instead:

my@lines="test-file".IO.slurp.lines; # also auto-chomps

Also, be aware that $! is not really relevant for file IO operation failures in Perl 6. An IO operation that fails will return a Failure instead of throwing an exception. If you want to return the failure message, it is in the failure itself, not in $!. To do similar IO error checking and reporting as in Perl 5:

my$fh=open('./bad/path/to/file', :w) ordie$fh;

Note: $fh instead of $!. Or, you can set $_ to the failure and die with $_:

my$fh=open('./bad/path/to/file', :w) orelse.die; # Perl 6

Any operation that tries to use the failure will cause the program to fault and terminate. Even just a call to the .self method is sufficient.

In Perl 6, you will probably want to run commands without using the shell:

my$arg='Hello';

my$captured=run('echo', $arg, :out).out.slurp;

my$captured=run(«echo"$arg"», :out).out.slurp;

You can also use the shell if you really want to:

my$arg='Hello';

my$captured=shell("echo $arg", :out).out.slurp;

my$captured=qqx{echo$arg};

But beware that in this case there is no protection at all! run does not use the shell, so there is no need to escape the arguments (arguments are passed directly). If you are using shell or qqx, then everything ends up being one long string which is then passed to the shell. Unless you validate your arguments very carefully, there is a high chance of introducing shell injection vulnerabilities with such code.

In Perl 5 one of the environment variables to specify extra search paths for Perl modules is PERL5LIB.

$PERL5LIB="/some/module/lib"perlprogram.pl

In Perl 6 this is similar, one merely needs to change a number! As you probably guessed, you just need to use PERL6LIB:

$PERL6LIB="/some/module/lib"perl6program.p6

In Perl 5 one uses the ':' (colon) as a directory separator for PERL5LIB, but in Perl 6 one uses the ',' (comma). For example:

$exportPERL5LIB=/module/dir1:/module/dir2;

but

$exportPERL6LIB=/module/dir1,/module/dir2;

(Perl 6 does not recognize either the PERL5LIB or the older Perl environment variable PERLLIB.)

As with Perl 5, if you don't specify PERL6LIB, you need to specify the library path within the program via the use lib pragma:

uselib'/some/module/lib'

Note that PERL6LIB is more of a developer convenience in Perl 6 (as opposed to the equivalent usage of PERL5LIB in Perl5) and shouldn't be used by module consumers as it could be removed in the future. This is because Perl 6's module loading isn't directly compatible with operating system paths.

In Perl 5 it is possible to selectively import functions from a given module like so:

useModuleNameqw{foo bar baz};

In Perl 6 one specifies the functions which are to be exported by using the is export role on the relevant subs; all subs with this role are then exported. Hence, the following module Bar exports the subs foo and bar but not baz:

unitmoduleBar;

subfoo($a) isexport{say"foo $a"}

subbar($b) isexport{say"bar $b"}

subbaz($z) {say"baz $z"}

To use this module, simply use Bar and the functions foo and bar will be available

useBar;

foo(1); #=> "foo 1"

bar(2); #=> "bar 2"

If one tries to use baz an "Undeclared routine" error is raised at compile time.

So, how does one recreate the Perl 5 behavior of being able to selectively import functions? By defining an EXPORT sub inside the module which specifies the functions to be exported and removing the module Bar statement.

The former module Bar now is merely a file called Bar.pm6 with the following contents:

subEXPORT(*@import-list) {

my%exportable-subs=

'&foo'=>&foo,

'&bar'=>&bar,

;

my%subs-to-export;

for@import-list->$import{

ifgrep$sub-name, %exportable-subs.keys{

%subs-to-export{$sub-name}=%exportable-subs{$sub-name};

}

}

return%subs-to-export;

}

subfoo($a, $b, $c) {say"foo, $a, $b, $c"}

subbar($a) {say"bar, $a"}

subbaz($z) {say"baz, $z"}

Note that the subs are no longer explicitly exported via the is export role, but by an EXPORT sub which specifies the subs in the module we want to make available for export and then we are populating a hash containing the subs which will actually be exported. The @import-list is set by the use statement in the calling code thus allowing us to selectively import the subs made available by the module.

So, to import only the foo routine, we do the following in the calling code:

useBar<foo>;

foo(1); #=> "foo 1"

Here we see that even though bar is exportable, if we don't explicitly import it, it's not available for use. Hence this causes an "Undeclared routine" error at compile time:

useBar<foo>;

foo(1);

bar(5); #!> "Undeclared routine: bar used at line 3"

However, this will work

useBar<foo bar>;

foo(1); #=> "foo 1"

bar(5); #=> "bar 5"

Note also that baz remains unimportable even if specified in the use statement:

useBar<foo bar baz>;

baz(3); #!> "Undeclared routine: baz used at line 2"

In order to get this to work, one obviously has to jump through many hoops. In the standard use-case where one specifies the functions to be exported via the is export role, Perl 6 automatically creates the EXPORT sub in the correct manner for you, so one should consider very carefully whether or not writing one's own EXPORT routine is worthwhile.

If you would like to export groups of functions from a module, you just need to assign names to the groups, and the rest will work automagically. When you specify is export in a sub declaration, you are in fact adding this subroutine to the :DEFAULT export group. But you can add a subroutine to another group, or to multiple groups:

unitmoduleBar;

subfoo() isexport{}# added by default to :DEFAULT

subbar() isexport(:FNORBL) {}# added to the FNORBL export group

subbaz() isexport(:DEFAULT:FNORBL) {}# added to both

So now you can use the Bar module like this:

useBar; # imports foo / baz

useBar:FNORBL; # imports bar / baz

useBar:ALL; # imports foo / bar / baz

Note that :ALL is an auto-generated group that encompasses all subroutines that have an is export trait.

There is also a Rakudo-specific debugging aid for developers called dd (Tiny Data Dumper, so tiny it lost the "t"). This will print the .perl representation plus some extra information that could be introspected, of the given variables on STDERR:

This project is dedicated to automated modernization of Perl code. It does not (yet) have a web front-end, and so must be locally installed to be useful. It also contains a separate program to translate Perl 5 regexes into Perl 6.

Jeff Goff's Perl::ToPerl6 module for Perl 5 is designed around Perl::Critic's framework. It aims to convert Perl5 to compilable (if not necessarily running) Perl 6 code with the bare minimum of changes. Code transformers are configurable and pluggable, so you can create and contribute your own transformers, and customize existing transformers to your own needs. You can install the latest release from CPAN, or follow the project live on GitHub. An online converter may become available at some point.