Python to Raku - nutshell
This page is an attempt to provide a way to learn Raku for folks coming from a Python background. We discuss the equivalent syntax in Raku for a number of Python constructs and idioms.
Basic syntax
Hello, world
Let's start with printing "Hello, world!". The put keyword in Raku 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!")
Raku
put "Hello, world!"
There is also the say keyword, which behaves similarly, but will call the gist method of its argument.
Raku
my $hello = "Hello, world!";
say $hello; # also prints "Hello, world!"
# same as: put $hello.gist
In Python, '
and "
are interchangeable.
In Raku, 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.
Raku
my $planet = 'earth';
say "Hello, $planet"; # OUTPUT: «Hello, earth»
say 'Hello, $planet'; # OUTPUT: «Hello, $planet»
say "Hello, planet number { 1 + 2 }"; # OUTPUT: «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 Raku, 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 )
Raku
say 1 + 2 +
3 + 4;
say 1 +
2;
Blocks
In Python, indentation is used to indicate a block. Raku uses curly braces.
Python
if 1 == 2:
print("Wait, what?")
else:
print("1 is not 2.")
Raku
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 Raku, 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 Raku 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
l = [1, 2, 3]
d = { 'a' : 12, 'b' : 99 }
print(s)
print(l[2])
print(d['a'])
# 10, 3, 12
Raku
my $s = 10;
my @l = 1, 2, 3;
my %d = a => 12, b => 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 Raku, every block creates a lexical scope.
Python
if True:
x = 10
print(x)
# x is now 10
Raku
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 1, 2, 3:
pass
print(x)
# x is 3
Raku
my \x = 10;
for 1, 2, 3 -> \x {
# do nothing
}
say x;
# x is 10
Lambdas in Python can be written as blocks or pointy blocks in Raku.
Python
l = lambda i: i + 12
Raku
my $l = -> $i { $i + 12 }
Another Raku idiom for constructing lambdas is the Whatever star, *
.
Raku
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
Raku
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, x²
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 Raku has an ASCII equivalent.
Control flow
Python has for
loops and while
loops:
for i in 1, 2:
print(i)
j = 1
while j < 3:
print(j)
j += 1
# 1, 2, 1, 2
Raku also has for
loops and while
loops:
for 1, 2 -> $i {
say $i
}
my $j = 1;
while $j < 3 {
say $j;
$j += 1
}
(Raku also has a few more looping constructs: repeat...until
,
repeat...while
, until
, and loop
.)
last
leaves a loop in Raku, and is analogous to
break
in Python. continue
in Python is next
in Raku.
Python
for i in range(10):
if i == 3:
continue
if i == 5:
break
print(i)
Raku
for ^10 -> $i {
next if $i == 3;
last if $i == 5;
say $i;
}
Using if
as a statement modifier (as above) is acceptable
in Raku, 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 Raku. These
both print 1, 2, 3.
Python
def count():
for i in 1, 2, 3:
yield i
for c in count():
print(c)
Raku
sub count {
gather {
for 1, 2, 3 -> $i {
take $i
}
}
}
for count() -> $c {
say $c;
}
Uses of Python's enumerate()
and .items()
mechanisms for
iterating lists or dict/maps can both be achieved using the same
kv method in Raku (because the "key" of a list is its
array-like numeric index):
Python
elems = ["neutronium", "hydrogen", "helium", "lithium"]
for i, e in enumerate(elems):
print("Elem no. %d is %s" % (i, e))
symbols = ["n", "H", "He", "Li"]
elem4Symbol = {s: e for s, e in zip(symbols, elems)}
for symbol, elem in elem4Symbol.items():
print("Symbol '%s' stands for %s" % (symbol, elem))
# Elem no. 0 is neutronium
# Elem no. 1 is hydrogen
# Elem no. 2 is helium
# Elem no. 3 is lithium
# Symbol 'H' stands for hydrogen
# Symbol 'He' stands for helium
# Symbol 'Li' stands for lithium
# Symbol 'n' stands for neutronium
Raku
my @elems = <neutronium hydrogen helium lithium>;
for @elems.kv -> $i, $e {
say "Elem no. $i is $e"
}
my @symbols = <n H He Li>;
my %elem-for-symbol;
%elem-for-symbol{@symbols} = @elems;
# Note that the iteration order will differ from Python
for %elem-for-symbol.kv -> $symbol, $element {
say "Symbol '$symbol' stands for $element";
}
Lambdas, functions and subroutines>
Declaring a function (subroutine) with def
in Python is accomplished
with sub
in Raku.
def add(a, b):
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 Raku, positional and named arguments are determined by the signature of the routine.
Python
def speak(word, times):
for i in range(times):
print word
speak('hi', 2)
speak(word='hi', times=2)
Raku
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
Raku supports multiple dispatch, so several signatures
could be made available by declaring a routine as a multi
.
multi speak($word, $times) {
say $word for ^$times
}
multi 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
Raku
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 Raku.
Python
print([ i * 2 for i in [3, 9]]) # OUTPUT: «[6, 18]»
Raku
say ( $_ * 2 for 3, 9 ); # OUTPUT: «(6 18)»
say ( { $^i * 2 } for 3, 9 ); # OUTPUT: «(6 18)»
say ( -> \i { i * 2 } for 3, 9 ); # 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 [1, 2, 3] if x > 1 ] # OUTPUT: «[4, 6]»
vs
say ( $_ * 2 if $_ > 1 for 1, 2, 3 ); # 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,9) X (2,10) ); # OUTPUT: «(5 13 11 19)»
say ( -> (\i, \j) { i + j } for (3,9) X (2,10) ); # OUTPUT: «(5 13 11 19)»
say ( -> ($i, $j) { $i + $j } for (3,9) X (2,10) );# OUTPUT: «(5 13 11 19)»
say ( { $^a[0] + $^a[1] } for (3,9) X (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 Raku:
Python:
class Dog:
def __init__(self, name):
self.name = name
Raku:
class Dog {
has $.name;
}
For each created class, Raku provides the constructor method new
by default which takes named arguments.
Python:
d = Dog('Fido')
e = Dog('Buddy')
print(d.name)
print(e.name)
Raku
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 Raku 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__(self, name):
self.name = name # instance attribute
d = Dog('Fido')
e = Dog('Buddy')
print(d.kind)
print(e.kind)
print(d.name)
print(e.name)
Raku:
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 Raku, you must use
the is rw
trait on the attributes:
Python:
class Dog:
def __init__(self, name):
self.name = name
d = Dog()
d.name = 'rover'
Raku:
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()
Raku
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(Animal, Friend, Pet):
pass
Raku
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 Raku, this is done with wrap
.
Python
def greeter(f):
def new():
print('hello')
f()
return new
@greeter
def world():
print('world')
world();
Raku
sub world {
say 'world'
}
&world.wrap(sub () {
say 'hello';
callsame;
});
world;
An alternative would be to use a trait:
# declare the trait 'greeter'
multi 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__(self, type, value, traceback):
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 Raku:
my $user_input = prompt("Say hi → ");
say $user_input; # OUTPUT: whatever you entered.
Tuples
Python tuples are immutable sequences. The sequence elements do not need to be of the same types.
Python
tuple1 = (1, "two", 3, "hat")
tuple2 = (5, 6, "seven")
print(tuple1[1]) # OUTPUT: «two»
tuple3 = tuple1 + tuple2
print(tuple3) # OUTPUT: «(1, 'two', 3, 'hat', 5, 6, 'seven')»
Raku
Raku does not have a builtin Tuple type. You can get the same behavior from Raku using the List type, or from an external module.
my $list1 = (1, "two", 3, "hat");
my $list2 = (5, 6, "seven");
say $list1[1]; # OUTPUT: «two»
my $list3 = (slip($list1), slip($list2));
my $list4 = (|$list1, |$list2); # equivalent to previous line
say $list3; # OUTPUT: «(1, two, 3, hat, 5, 6, seven)»