Python to Perl 6 - nutshell

Learning Perl 6 from Python, in a nutshell

This page is an attempt to provide a way to learn Perl 6 for folks coming from a Python background. We discuss the equivalent syntax in Perl 6 for a number of Python constructs and idioms.

Basic syntax

Hello, world

Let's start with printing "Hello, world!". The put keyword in Perl 6 is the equivalent of print in Python. Like Python 2, parentheses are optional. A newline is added to the end of the line.

Python 2

print "Hello, world!"

Python 3

print("Hello, world!")

Perl 6

put "Hello, world!"

There is also the say keyword, which behaves similarly, but will call the gist method of its argument.

Perl 6

my $hello = "Hello, world!";
say $hello;  # also prints "Hello, world!" 
             # same as: put $hello.gist

In Python, ' and " are interchangeable. In Perl 6, both may be used for quoting, but double quotes (") signify that interpolation should be performed. For instance, variables that start with a $, and expressions contained in curly braces are interpolated.

Perl 6

my $planet = 'earth';
say "Hello, $planet";   # Hello, earth 
say 'Hello, $planet';   # Hello, $planet 
say "Hello, planet number { 1 + 2 }"# Hello, planet number 3

Statement separators

In Python, a newline signifies the end of a statement. There are a few exceptions: A backslash before a newline continues a statement across lines. Also if there is an unmatched opening parentheses, square bracket, or curly brace, the statement continues across lines, until the matching curly braces are closed.

In Perl 6, a semicolon signifies the end of a statement. The semicolon may be omitted if it is the last statement of a block. The semicolon may also be omitted if there is a closing curly brace followed by a newline.

Python

print 1 + 2 + \
    3 + 4
print ( 1 +
    2 )

Perl 6

say 1 + 2 +
    3 + 4;
say 1 +
    2;

Blocks

In Python, indentation is used to indicate a block. Perl 6 uses curly braces.

Python

if 1 == 2:
    print "Wait, what?"
else:
    print "1 is not 2."

Perl 6

if 1 == 2 {
    say "Wait, what?"
} else {
    say "1 is not 2."
}

Parentheses are optional in both languages for expressions in conditionals, as shown above.

Variables

In Python, variables are declared and initialized at the same time:

foo = 12
bar = 19

In Perl 6, the my declarator declares a lexical variable. A variable can be initialized with =. This variable can either be declared first and later initialized or declared and initialized at once.

my $foo;       # declare 
$foo = 12;     # initialize 
my $bar = 19;  # both at once

Also, as you may have noticed, variables in Perl 6 usually start with sigils -- symbols indicating the type of their container. Variables starting with a $ hold scalars. Variables starting with an @ hold arrays, and variables starting with a % hold a hash (dict). Sigilless variables, declared with a \ but used without them, are bound to the value they are assigned to and are thus immutable.

Please note that, from now on, we are going to use sigilless variables in most examples just to illustrate the similarity with Python. That is technically correct, but in general we are going to use sigilless variables in places where their immutability (or independence of type, when they are used in signatures) is needed or needs to be highlighted.

Python

s = 10
= [123]
d = { a : 12b : 99 }
 
print s 
print l[2]
print d['a']
# 10, 2, 12 

Perl 6

my $s = 10;
my @l = 123;
my %d = => 12=> 99;
my \x = 99;
 
say $s;
say @l[1];
say %d<a>;  # or %d{'a'} 
say x;
# 10, 2, 12, 99

Scope

In Python, functions and classes create a new scope, but no other block constructor (e.g. loops, conditionals) creates a scope. In Python 2, list comprehensions do not create a new scope, but in Python 3, they do.

In Perl 6, every block creates a lexical scope.

Python

if True:
    x = 10
print x
# x is now 10 

Perl 6

if True {
    my $x = 10
}
say $x
# error, $x is not declared in this scope 
my $x;
if True {
    $x = 10
}
say $x
# ok, $x is 10 

Python

x = 10
for x in 123:
   pass
print x
# x is 3 

Perl 6

my \x = 10;
for 123 -> \x {
    # do nothing 
}
say x;
# x is 10

Lambdas in Python can be written as blocks or pointy blocks in Perl 6.

Python

l = lambda i: i + 12

Perl 6

my $l = -> $i { $i + 12 }

Another Perl 6 idiom for constructing lambdas is the Whatever star, *.

Perl 6

my $l = * + 12    # same as above

A * in an expression will become a placeholder for the argument, and transform the expression into a lambda at compile time. Each * in an expression is a separate positional parameter.

See the section below for more constructs regarding subroutines and blocks.

Another example (from the Python FAQ):

Python

squares = []
for x in range(5):
    squares.append(lambda: x ** 2)
print squares[2]()
print squares[4]()
# both 16 since there is only one x 

Perl 6

my \squares = [];
for ^5 -> \x {
    squares.append({ x² });
}
say squares[2]();
say squares[4]();
# 4, 16 since each loop iteration has a lexically scoped x,

Note that ^N is like range(N). Similarly, N..^M works like range(N, M) (a list from N to M - 1). The range N..M is a list from N to M. The ^ before or after the .. indicates that the beginning or ending endpoint of the list (or both) should be excluded.

Also, is a cute way of writing x ** 2 (which also works fine); the unicode superscript 2 squares a number. Many of the other unicode operators work as you would expect (exponents, fractions, π), but every unicode operator or symbol that can be used in Perl 6 has an ASCII equivalent.

Control flow

Python has for loops and while loops:

for i in 12:
    print i
j = 1
while j < 3:
    print j
    j += 1

# 1, 2, 1, 2

Perl 6 also has for loops and while loops:

for 12 -> $i {
    say $i
}
my $j = 1;
while $j < 3 {
    say $j;
    $j += 1
}

(Perl 6 also has a few more looping constructs: repeat...until, repeat...while, until, and loop.)

last leaves a loop in Perl 6, and is analogous to break in Python. continue in Python is next in Perl 6.

Python

for i in range(10):
    if i == 3:
        continue
    if i == 5:
        break
    print i

Perl 6

for ^10 -> $i {
    next if $i == 3;
    last if $i == 5;
    say $i;
}

Using if as a statement modifier (as above) is acceptable in Perl 6, even outside of a list comprehension.

The yield statement within a for loop in Python, which produces a generator, is like a gather/take construct in Perl 6. These both print 1, 2, 3.

Python

def count():
    for i in 123:
        yield i
 
for c in count():
    print c

Perl 6

sub count {
    gather {
        for 123 -> $i {
            take $i
        }
    }
}
 
for count() -> $c {
    say $c;
}

Lambdas, functions and subroutines

Declaring a function (subroutine) with def in Python is accomplished with sub in Perl 6.

def add(ab):
    return a + b
 
sub add(\a, \b{
    return a + b
}

The return is optional; the value of the last expression is used as the return value:

sub add(\a, \b{
    a + b
}
# using variables with sigils 
sub add($a$b{
    $a + $b
}

Python 2 functions can be called with positional arguments or keyword arguments. These are determined by the caller. In Python 3, some arguments may be "keyword only". In Perl 6, positional and named arguments are determined by the signature of the routine.

Python

def speak(wordtimes):
    for i in range(times):
        print word
speak('hi'2)
speak(word='hi'times=2)

Perl 6

Positional parameters:

sub speak($word$times{
  say $word for ^$times
}
speak('hi'2);

Named parameters start with a colon:

sub speak(:$word:$times{
  say $word for ^$times
}
speak(word => 'hi'times => 2);
speak(:word<hi>:times<2>);      # Alternative, more idiomatic

Perl 6 supports multiple dispatch, so several signatures could be made available by declaring a routine as a multi.

multi sub speak($word$times{
  say $word for ^$times
}
multi sub speak(:$word:$times{
    speak($word$times);
}
speak('hi'2);
speak(:word<hi>:times<2>);

Named parameters can be sent using a variety of formats:

sub hello {...};
# all the same 
hello(name => 'world'); # fat arrow syntax 
hello(:name('world'));  # pair constructor 
hello :name<world>;     # <> quotes words and makes a list 
my $name = 'world';
hello(:$name);          # lexical var with the same name

Creating an anonymous function can be done with sub, with a block or with a pointy block.

Python

square = lambda x: x ** 2

Perl 6

my $square = sub ($x{ $x ** 2 };  # anonymous sub 
my $square = -> $x { $x ** 2 };     # pointy block 
my $square = { $^x ** 2 };          # placeholder variable 
my $square = { $_ ** 2 };           # topic variable

Placeholder variables are lexicographically ordered to form positional parameters. Thus these are the same:

my $power = { $^x ** $^y };
my $power = -> $x$y { $x ** $y };

List comprehensions

Postfix statement modifiers and blocks can be combined to easily create list comprehensions in Perl 6.

Python

print [ i * 2 for i in 39 ]                      # OUTPUT: «[6, 18]␤» 

Perl 6

say ( $_ * 2 for 39 );                           # OUTPUT: «(6 18)␤» 
say ( { $^i * 2 } for 39 );                      # OUTPUT: «(6 18)␤» 
say ( -> \i { i * 2 } for 39 );                  # OUTPUT: «(6 18)␤»

Conditionals can be applied, but the if keyword comes first, unlike in Python where the if comes second.

print [ x * 2 for x in 123 if x > 1 ]          # OUTPUT: «[4, 6]␤» 

vs

say ( $_ * 2 if $_ > 1 for 123 );              # OUTPUT: «(4 6)␤»

For nested loops, the cross product operator X will help:

print [ i + j for i in 3,9 for j in 2,10 ]         # OUTPUT: «[5, 13, 11, 19]␤» 

becomes either of these:

say ( { $_[0+ $_[1} for (3,9X (2,10) );      # OUTPUT: «(5 13 11 19)␤» 
say ( -> (\i, \j{ i + j } for (3,9X (2,10) );  # OUTPUT: «(5 13 11 19)␤» 
say ( -> ($i$j{ $i + $j } for (3,9X (2,10) );# OUTPUT: «(5 13 11 19)␤» 
say ( { $^a[0+ $^a[1} for (3,9X (2,10) );    # OUTPUT: «(5 13 11 19)␤»

Using map (which is just like Python's map) and grep (which is like Python's filter) is an alternative.

Classes and objects

Here's an example from the Python docs. First let's go over "instance variables" which are known as attributes in Perl 6:

Python:

class Dog:
    def __init__(selfname):
        self.name = name

Perl 6:

class Dog {
    has $.name;
}

For each created class, Perl 6 provides the constructor method new by default which takes named arguments.

Python:

d = Dog('Fido')
e = Dog('Buddy')
print d.name
print e.name

Perl 6

my $d = Dog.new(:name<Fido>); # or: Dog.new(name => 'Fido') 
my $e = Dog.new(:name<Buddy>);
say $d.name;
say $e.name;

Class attributes in Perl 6 can be declared in a few ways. One way is to just declare a lexical variable and a method for accessing it.

Python:

class Dog:
    kind = 'canine'                # class attribute 
    def __init__(selfname):
        self.name = name           # instance attribute 
d = Dog('Fido')
e = Dog('Buddy')
print d.kind
print e.kind
print d.name
print e.name

Perl 6:

class Dog {
    my $kind = 'canine';           # class attribute 
    method kind { $kind }
    has $.name;                    # instance attribute 
}
 
my $d = Dog.new(:name<Fido>);
my $e = Dog.new(:name<Buddy>);
say $d.kind;
say $e.kind;
say $d.name;
say $e.name;

In order to mutate attributes in Perl 6, you must use the is rw trait on the attributes:

Python:

class Dog:
    def __init__(selfname):
        self.name = name
d = Dog()
d.name = 'rover'

Perl 6:

class Dog {
    has $.name is rw;
}
my $d = Dog.new;
$d.name = 'rover';

Inheritance is done using is:

Python

class Animal:
    def jump(self):
        print ("I am jumping")
 
class Dog(Animal):
    pass
 
d = Dog()
d.jump()

Perl 6

class Animal {
    method jump {
        say "I am jumping"
    }
}
 
class Dog is Animal {
}
 
my $d = Dog.new;
$d.jump;

Multiple inheritance is possible by using the is trait as many times as required. Alternatively, it can be used in conjunction with the also keyword.

Python

class Dog(AnimalFriendPet):
    pass

Perl 6

class Animal {}class Friend {}class Pet {};
...;
class Dog is Animal is Friend is Pet {};

or

class Animal {}class Friend {}class Pet {};
...;
class Dog is Animal {
    also is Friend;
    also is Pet;
    ...
}

Decorators

Decorators in Python are a way of wrapping a function in another one. In Perl 6, this is done with wrap.

Python

def greeter(f):
    def new():
        print 'hello'
        f()
    return new
 
@greeter
def world():
    print 'world'
 
world();

Perl 6

sub world {
    say 'world'
}
 
&world.wrap(sub () {
    say 'hello';
    callsame;
});
 
world;

An alternative would be to use a trait:

# declare the trait 'greeter' 
multi sub trait_mod:<is>(Routine $r:$greeter{
    $r.wrap(sub {
        say 'hello';
        callsame;
    })
}
 
sub world is greeter {
    say 'world';
}
 
world;

Context managers

Context managers in Python declare actions that happen when entering or exiting a scope.

Here's a Python context manager that prints the strings 'hello', 'world', and 'bye'.

class hello:
    def __exit__(selftypevaluetraceback):
        print 'bye'
    def __enter__(self):
        print 'hello'
 
with hello():
    print 'world'

For "enter" and "exit" events, passing a block as an argument would be one option:

sub hello(Block $b{
    say 'hello';
    $b();
    say 'bye';
}
 
hello {
    say 'world';
}

A related idea is 'Phasers' which may be set up to run on entering or leaving a block.

{
    LEAVE say 'bye';
    ENTER say 'hello';
    say 'world';
}

input

In Python 3, the input keyword is used to prompt the user. This keyword can be provided with an optional argument which is written to standard output without a trailing newline:

user_input = input("Say hi → ")
print(user_input)

When prompted, you can enter Hi or any other string, which will be stored in the user_input variable. This is similar to prompt in Perl 6:

my $user_input = prompt("Say hi → ");
say $user_input# OUTPUT: whatever you entered.