This document is intended to give you a quick overview of the Raku programming language.
For those new to Raku, it should get you up and running.

Some sections of this document refer to other (more complete and accurate) parts of the Raku documentation. You should read them if you need more information on a specific subject.

Throughout this document, you will find examples for most discussed topics. To better understand them, take the time to reproduce all examples.

License

This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit

Contribution

If you would like to contribute to this document, head over to:

Feedback

All feedback is welcomed: naoum@hankache.com

If you liked this work, Star the repository on GitHub.

1. Introduction

1.1. What is Raku

Raku is a high-level, general-purpose, gradually typed language. Raku is multi-paradigmatic. It supports procedural, object oriented, and functional programming.

Raku motto:
  • TMTOWTDI (Pronounced Tim Toady): There is more than one way to do it.

1.2. Jargon

  • Raku: Is a language specification with a test suite. Implementations that pass the specification test suite are considered Raku.

  • Rakudo: Is a compiler for Raku.

  • Zef: Is a Raku module installer.

  • Rakubrew: Is a Raku installer and version manager.

1.3. Installing Raku

There are several ways of installing Raku.

1.3.1. Rakubrew

The recommended way of installing Raku is through Rakubrew, a Raku installer and version manager.
It downloads a ready to use version of Rakudo, and it works on Linux, macOS and Windows.

First install Rakubrew by running one of the following commands, depending on your OS.

On Linux, from your terminal:
curl https://rakubrew.org/install-on-perl.sh | sh
On macOS, from your terminal:
curl https://rakubrew.org/install-on-macos.sh | sh
On Windows, from a PowerShell window:
. {iwr -useb https://rakubrew.org/install-on-powershell.ps1 } | iex

The script ends by displaying a command that makes Rakubrew available each time you open a terminal.
Copy that command and run it, then close your terminal and open a new one.

Now install the latest version of Raku:

rakubrew download

To make sure that Raku is installed, run:

raku --version

1.3.2. Binary releases

Rakudo also provides binary releases for Linux, macOS and Windows.
They can be downloaded from https://rakudo.org/downloads

Windows

Get the installer (file with .msi extension) and run it.
The installer adds Raku and Zef to the PATH.

Linux and macOS

Get the archive (file with .tar.gz extension) and extract it.
The executables are located in the bin and share/perl6/site/bin folders of the archive. Add both folders to your PATH.

1.3.3. Rakudo Star

Rakudo Star is a bundle that includes Rakudo, Zef, a collection of Raku modules, and documentation.
It can be installed using one of the following third party packages:

  • Docker: docker pull rakudo-star

  • Scoop on Windows: scoop install rakudo-star

  • Homebrew on macOS: brew install rakudo-star

For other options, go to https://raku.org/install

1.4. Running Raku code

Running Raku code can be done using the REPL (Read-Eval-Print Loop). To do this, open a terminal, type raku into the terminal window, and hit [Enter]. This will cause a prompt of > to appear. Next, type a line of code and hit [Enter]. The REPL will print out the value of the line. You may then type another line, or type exit and hit [Enter] to leave the REPL.

Alternatively, write your code in a file, save it and run it. It is recommended that Raku scripts have a .raku file name extension. Run the file by typing raku filename.raku into the terminal window and hitting [Enter]. Unlike the REPL, this will not automatically print the result of each line: the code must contain a statement like say to print output.

The REPL is mostly used for trying a specific piece of code, typically a single line. For programs with more than a single line it is recommended to store them in a file and then run them.

Single lines may also be tried non-interactively on the command-line by typing raku -e 'your code here' and hitting [Enter].

A line editor helps you get the most out of the REPL. It lets you use the up and down arrows for history, left and right to edit input, and TAB for completion.

If these features are not enabled, consider running the following command and you shall be all set:

zef install Terminal::LineEditor

1.5. Editors

Since most of the time we will be writing and storing our Raku programs in files, we should have a decent text editor that recognizes Raku syntax.

The community frequently uses the following editors:

1.6. Hello World!

We shall begin with the hello world ritual.

say 'hello world';

that can also be written as:

'hello world'.say;

1.7. Syntax overview

Raku is free form: Most of the time you are free to use any amount of whitespace, although in certain cases whitespace carries meaning.

Statements are typically a single line of code separated with semicolons:

say "Hello" if True;
say "World" if False;

Semicolons are not required after the last statement in a file or block of code, but it’s good practice to include them anyway.

Blocks can contain a collection of statements. Surround statements with curly braces to create a block:

{
  say "First statement in the block.";
  say "Second statement in the block.";
}

Expressions are a special type of statement that returns a value: 1+2 will return 3

Expressions are made of terms and operators.

Terms are:

  • Variables: A value that can be manipulated and changed.

  • Literals: A constant value like a number or a string.

Operators are classified into types:

Type

Explanation

Example

Prefix

Before the term.

++1

Infix

Between terms

1+2

Postfix

After the term

1++

Circumfix

Around the term

(1)

Postcircumfix

After one term, around another

Array[1]

1.7.1. Identifiers

Identifiers are the names given to terms when you define them.

Rules:
  • They must start with an alphabetic character or an underscore.

  • They can contain digits (except the first character).

  • They can contain dashes or apostrophes (except the first and last character), provided there’s an alphabetic character to the right side of each dash or apostrophe.

Valid

Invalid

var1

1var

var-one

var-1

var’one

var'1

var1_

var1'

_var

-var

Naming conventions:
  • Camel case: variableNo1

  • Kebab case: variable-no1

  • Snake case: variable_no1

You are free to name your identifiers as you like, but it is good practice to adopt one naming convention consistently.

Using meaningful names will ease your (and others') programming life.

  • var1 = var2 * var3 is syntactically correct but its purpose is not evident.

  • monthly-salary = daily-rate * working-days would be a better way to name your variables.

1.7.2. Comments

A comment is text ignored by the compiler and used as a note.

Comments are divided into 3 types:

  • Single line:

    # This is a single line comment
  • Embedded:

    say #`(This is an embedded comment) "Hello World.";
  • Multi line:

    =begin comment
    This is a multi line comment.
    Comment 1
    Comment 2
    =end comment

1.7.3. Quotes

Strings need to be delimited by either double quotes or single quotes.

Always use double quotes:

  • if your string contains an apostrophe.

  • if your string contains a variable that needs to be interpolated.

say 'Hello World';   # Hello World
say "Hello World";   # Hello World
say "Don't";         # Don't
my $name = 'John Doe';
say 'Hello $name';   # Hello $name
say "Hello $name";   # Hello John Doe

2. Operators

Operators are the symbols and words that combine values into expressions, like + in 1 + 2

2.1. Common operators

The below tables list the most commonly used operators.

2.1.1. Arithmetic

Operator Type Description Example Result

+

Infix

Addition

1 + 2

3

-

Infix

Subtraction

3 - 1

2

*

Infix

Multiplication

3 * 2

6

**

Infix

Power

3 ** 2

9

/

Infix

Division

3 / 2

1.5

div

Infix

Integer Division (rounds down)

3 div 2

1

%

Infix

Modulo

7 % 4

3

%%

Infix

Divisibility

6 %% 4

False

6 %% 3

True

gcd

Infix

Greatest common divisor

6 gcd 9

3

lcm

Infix

Least common multiple

6 lcm 9

18

2.1.2. Comparison

Operator Type Description Example Result

==

Infix

Numeric equal

9 == 7

False

!=

Infix

Numeric not equal

9 != 7

True

<

Infix

Numeric less than

9 < 7

False

>

Infix

Numeric greater than

9 > 7

True

<=

Infix

Numeric less than or equal

7 <= 7

True

>=

Infix

Numeric greater than or equal

9 >= 7

True

<=>

Infix

Numeric three-way comparator

1 <=> 1.0

Same

1 <=> 2

Less

3 <=> 2

More

eq

Infix

String equal

"John" eq "John"

True

ne

Infix

String not equal

"John" ne "Jane"

True

lt

Infix

String less than

"a" lt "b"

True

gt

Infix

String greater than

"a" gt "b"

False

le

Infix

String less than or equal

"a" le "a"

True

ge

Infix

String greater than or equal

"a" ge "b"

False

leg

Infix

String three-way comparator

"a" leg "a"

Same

"a" leg "b"

Less

"c" leg "b"

More

cmp

Infix

Smart three-way comparator

"a" cmp "b"

Less

3.5 cmp 2.6

More

~~

Infix

Smart match

2 ~~ 2

True

2 ~~ Int

True

"Raku" ~~ "Raku"

True

"Raku" ~~ Str

True

"enlightenment" ~~ /light/

「light」

2.1.3. Assignment and strings

Operator Type Description Example Result

=

Infix

Assignment

my $var = 7

Assigns the value of 7 to the variable $var

~

Infix

String concatenation

9 ~ 7

97

"Hi " ~ "there"

Hi there

x

Infix

String replication

13 x 3

131313

"Hello " x 3

Hello Hello Hello

2.1.4. Increment and decrement

Operator Type Description Example Result

++

Prefix

Increment

my $var = 2; ++$var;

Increment the variable by 1 and return the result 3

Postfix

Increment

my $var = 2; $var++;

Return the variable 2 and then increment it

--

Prefix

Decrement

my $var = 2; --$var;

Decrement the variable by 1 and return the result 1

Postfix

Decrement

my $var = 2; $var--;

Return the variable 2 and then decrement it

2.1.5. Conversion

Operator Type Description Example Result

+

Prefix

Coerce the operand to a numeric value

+"3"

3

+True

1

+False

0

-

Prefix

Coerce the operand to a numeric value and return the negation

-"3"

-3

-True

-1

-False

0

?

Prefix

Coerce the operand to a boolean value

?0

False

?9.8

True

?"Hello"

True

?""

False

my $var; ?$var;

False

my $var = 7; ?$var;

True

!

Prefix

Coerce the operand to a boolean value and return the negation

!4

False

2.1.6. Ranges and lists

Operator Type Description Example Result

..

Infix

Range Constructor

0..5

Creates a range of the interval [0, 5] [1]

..^

Infix

Range Constructor

0..^5

Creates a range of the interval [0, 5) [1]

^..

Infix

Range Constructor

0^..5

Creates a range of the interval (0, 5] [1]

^..^

Infix

Range Constructor

0^..^5

Creates a range of the interval (0, 5) [1]

^

Prefix

Range Constructor

^5

Same as 0..^5 Creates a range of the interval [0, 5) [1]

...

Infix

Sequence Constructor

0...9999

return the elements only if requested

|

Prefix

Flattening

|(0..5)

(0 1 2 3 4 5)

|(0^..^5)

(1 2 3 4)

2.2. Logical operators

Logical operators combine conditions or choose between two values.

Operator Type Description Example Result

and

Infix

True if both operands are true

True and True

True

True and False

False

or

Infix

True if at least one operand is true

True or False

True

False or False

False

&&

Infix

Same as and written as a symbol

True && False

False

||

Infix

Same as or written as a symbol

True || False

True

//

Infix

Defined-or: the left operand if it has a value, otherwise the right operand

my $var; $var // 5;

5

my $var = 3; $var // 5;

3

?? !!

Ternary

Conditional: if the condition is true the first value, otherwise the second

7 > 5 ?? "yes" !! "no"

yes

7 < 5 ?? "yes" !! "no"

no

Logical operators are mostly used to combine comparisons:

my $age = 30;
say $age >= 18 && $age <= 65;   # True
say $age < 18 || $age > 65;     # False

The defined-or operator // is a convenient way of providing a default value:

my $name;
say $name // 'Anonymous';       # Anonymous

2.3. Reversed operators

Adding R before any operator will have the effect of reversing its operands.

Normal operation Result Reversed operator Result

2 / 3

0.666667

2 R/ 3

1.5

2 - 1

1

2 R- 1

-1

2.4. Reduction operators

Reduction operators work on lists of values. They are formed by surrounding the operator with brackets []

Normal operation Result Reduction operator Result

1 + 2 + 3 + 4 + 5

15

[+] 1, 2, 3, 4, 5

15

1 * 2 * 3 * 4 * 5

120

[*] 1, 2, 3, 4, 5

120

For the complete list of operators, including their precedence, go to https://docs.raku.org/language/operators

3. Variables

Raku variables are classified into 3 categories: scalars, arrays and hashes.

A sigil (Sign in Latin) is a character that is used as a prefix to categorize variables.

  • $ is used for scalars

  • @ is used for arrays

  • % is used for hashes

A variable has to be declared before it is used.
This is done using the keyword my followed by the name of the variable.

This guide offers a simplified model of variables suitable for learning the basics of Raku. For a deeper understanding of variables, see https://docs.raku.org/language/containers

3.1. Scalars

A scalar holds one value or reference.

# String
my $name = 'John Doe';
say $name;

# Integer
my $age = 99;
say $age;

A specific set of operations can be performed on a scalar, depending on the value it holds.

3.1.1. String

my $name = 'John Doe';
say $name.uc;
say $name.chars;
say $name.flip;
Output
JOHN DOE
8
eoD nhoJ
For the complete list of methods applicable to strings, see https://docs.raku.org/type/Str

3.1.2. Integer

my $age = 17;
say $age.is-prime;
Output
True
For the complete list of methods applicable to integers, see https://docs.raku.org/type/Int

3.1.3. Rational number

my $age = 2.3;
say $age.numerator;
say $age.denominator;
say $age.nude;
Output
23
10
(23 10)
For the complete list of methods applicable to rational numbers, see https://docs.raku.org/type/Rat

3.2. Arrays

Arrays are lists containing multiple values.

my @animals = 'camel', 'llama', 'owl';
say @animals;

Many operations can be performed on arrays as shown in the below example:

The tilde ~ is used for string concatenation.
Script
my @animals = 'camel', 'vicuña', 'llama';
say "The zoo contains " ~ @animals.elems ~ " animals";
say "The animals are: " ~ @animals;
say "I will adopt an owl for the zoo";
@animals.push("owl");
say "Now my zoo has: " ~ @animals;
say "The first animal we adopted was the " ~ @animals[0];
@animals.pop;
say "Unfortunately the owl got away and we're left with: " ~ @animals;
say "We're closing the zoo and keeping one animal only";
say "We're going to let go: " ~ @animals.splice(1, 2) ~ " and keep the " ~ @animals;
Output
The zoo contains 3 animals
The animals are: camel vicuña llama
I will adopt an owl for the zoo
Now my zoo has: camel vicuña llama owl
The first animal we adopted was the camel
Unfortunately the owl got away and we're left with: camel vicuña llama
We're closing the zoo and keeping one animal only
We're going to let go: vicuña llama and keep the camel
Explanation

.elems returns the number of elements in an array.
.push() adds one or more elements to the array.
We can access a specific element in the array by specifying its position @animals[0].
.pop removes the last element from the array and returns it.
.splice(a, b) will remove b elements starting at position a.

Below are a few more operations that can be performed on arrays:

Script
my @animals = 'camel', 'vicuña', 'llama';
say "The last animal is the " ~ @animals[*-1];
say "The first two animals are: " ~ @animals[0, 1];
say "All the animals: " ~ @animals.join(', ');
@animals.unshift("owl");
say "An owl joined at the front: " ~ @animals;
@animals.shift;
say "The owl got away again: " ~ @animals;
Output
The last animal is the llama
The first two animals are: camel vicuña
All the animals: camel, vicuña, llama
An owl joined at the front: owl camel vicuña llama
The owl got away again: camel vicuña llama
Explanation

@animals[*-1] returns the last element of the array.
@animals[0, 1] returns several elements at once. This is called a slice.
.join(', ') returns a single string made of all the elements, separated by a comma and a space.
.unshift() adds one or more elements to the beginning of the array.
.shift removes the first element from the array and returns it.

3.2.1. Changing an array in place

Some methods change the array they are called on. Others leave it as it is and return a new value.
It is important to know which is which.

Script
my @numbers = 7, 2, 4, 9, 11, 3;

@numbers.push(99);
say @numbers;      #1

say @numbers.sort; #2
say @numbers;      #3

@numbers .= sort;
say @numbers;      #4
Output
[7 2 4 9 11 3 99] #1
(2 3 4 7 9 11 99) #2
[7 2 4 9 11 3 99] #3
[2 3 4 7 9 11 99] #4
Explanation

.push changes the array. A method that does this is called a mutator (#1)

.sort does not change the array. It returns the sorted values and leaves the original as it was:

  • (#2) shows the sorted values that it returned.

  • (#3) shows that the array is still unmodified.

To store the result back in the array, we use .= instead of . (#4)
@numbers .= sort is a shorter way of writing @numbers = @numbers.sort

The result in #2 is shown between parentheses instead of square brackets because .sort returns a sequence, not an array. Once it is stored back in @numbers it is an array again.
.= works with any method and on any variable. For example, $name .= uc replaces the value of $name with its uppercase version.

3.2.2. Fixed-size arrays

A basic array is declared as following:

my @array;

The basic array can have indefinite length and thus is called auto-extending.
The array will accept any number of values with no restriction.

In contrast, we can also create fixed-size arrays.
These arrays cannot be accessed beyond their defined size.

To declare an array of fixed size, specify its maximum number of elements in square brackets immediately after its name:

my @array[3];

This array will be able to hold a maximum of 3 values, indexed from 0 to 2.

my @array[3];
@array[0] = "first value";
@array[1] = "second value";
@array[2] = "third value";

You will not be able to add a fourth value to this array:

my @array[3];
@array[0] = "first value";
@array[1] = "second value";
@array[2] = "third value";
@array[3] = "fourth value";
Output
Index 3 for dimension 1 out of range (must be 0..2)

3.2.3. Multidimensional arrays

The arrays we saw until now are one-dimensional.
Fortunately, we can define multi-dimensional arrays in Raku.

my @tbl[3;2];

This array is two-dimensional. The first dimension can have a maximum of 3 values and the second dimension a maximum of 2 values.

Think of it as a 3x2 grid.

my @tbl[3;2];
@tbl[0;0] = 1;
@tbl[0;1] = "x";
@tbl[1;0] = 2;
@tbl[1;1] = "y";
@tbl[2;0] = 3;
@tbl[2;1] = "z";
say @tbl;
Output
[[1 x] [2 y] [3 z]]
Visual representation of the array:
[1 x]
[2 y]
[3 z]
For the complete Array reference, see https://docs.raku.org/type/Array

3.3. Hashes

3.3.1. Creating a hash

A hash is a set of key/value pairs.
my %capitals = 'UK', 'London', 'Germany', 'Berlin';
say %capitals;
Another succinct way of filling the hash:
my %capitals = UK => 'London', Germany => 'Berlin';
say %capitals;

3.3.2. Common methods

Some of the methods that can be called on hashes are:

Script
my %capitals = UK => 'London', Germany => 'Berlin';
%capitals.push: (France => 'Paris');
say %capitals.kv;
say %capitals.keys;
say %capitals.values;
say "The capital of France is: " ~ %capitals<France>;
Output
(France Paris Germany Berlin UK London)
(France Germany UK)
(Paris Berlin London)
The capital of France is: Paris
Explanation

.push: (key => 'Value') adds a new key/value pair.
.kv returns a list containing all keys and values.
.keys returns a list that contains all keys.
.values returns a list that contains all values.
We can access a specific value in the hash by specifying its key %hash<key>

Raku does not keep the keys of a hash in a fixed order. The order of the countries in your output can be different from the one above, and it can change each time the script runs.

3.3.3. Checking and removing a key

We can also check if a key exists and remove a key from the hash:

Script
my %capitals = UK => 'London', Germany => 'Berlin';
say %capitals<Germany>:exists;
say %capitals<France>:exists;
%capitals<Germany>:delete;
say %capitals;
Output
True
False
{UK => London}
Explanation

%hash<key>:exists returns True if the key is in the hash.
%hash<key>:delete removes the key and its value from the hash.

For the complete Hash reference, see https://docs.raku.org/type/Hash

3.4. Types

3.4.1. Dynamic and static typing

In the previous examples, we did not specify what type of values the variables should hold.

.WHAT will return the type of value held in a variable.
my $var = 'Text';
say $var;
say $var.WHAT;

$var = 123;
say $var;
say $var.WHAT;

As you can see in the above example, the type of value in $var was once (Str) and then (Int).

This style of programming is called dynamic typing. Dynamic in the sense that variables may contain values of any type.

Now try running the below example:
Notice Int before the variable name.

my Int $var = 'Text';
say $var;
say $var.WHAT;

It will fail and return this error message: Type check failed in assignment to $var; expected Int but got Str

What happened is that we specified beforehand that the variable should be of type (Int). When we tried to assign a (Str) to it, it failed.

This style of programming is called static typing. Static in the sense that variable types are defined before assignment and cannot change.

Raku is classified as gradually typed; it allows both static and dynamic typing.

3.4.2. Typed arrays and hashes

Arrays and hashes can also be statically typed:

my Int @array = 1, 2, 3;
say @array;
say @array.WHAT;

my Str @multilingual = "Hello", "Salut", "Hallo", "您好", "안녕하세요", "こんにちは";
say @multilingual;
say @multilingual.WHAT;

my Str %capitals = UK => 'London', Germany => 'Berlin';
say %capitals;
say %capitals.WHAT;

my Int %country-codes = UK => 44, Germany => 49;
say %country-codes;
say %country-codes.WHAT;

3.4.3. Common types

Below is a list of the most commonly used types:
You will most probably never use the first two but they are listed for informational purpose.

Type Description Example Result

Mu

The root of the Raku type hierarchy

Any

Default base class for new classes and for most built-in classes

Cool

Value that can be treated as a string or number interchangeably

my Cool $var = 31; say $var.flip; say $var * 2;

13 62

Str

String of characters

my Str $var = "NEON"; say $var.flip;

NOEN

Int

Integer (arbitrary-precision)

7 + 7

14

Rat

Rational number (limited-precision)

0.1 + 0.2

0.3

Bool

Boolean

!True

False

3.5. Introspection

Introspection is the process of getting information about an object’s properties, like its type.
In one of the previous examples we used .WHAT to return the type of the variable.

my Int $var;
say $var.WHAT;    # (Int)
my $var2;
say $var2.WHAT;   # (Any)
$var2 = 1;
say $var2.WHAT;   # (Int)
$var2 = "Hello";
say $var2.WHAT;   # (Str)
$var2 = True;
say $var2.WHAT;   # (Bool)
$var2 = Nil;
say $var2.WHAT;   # (Any)

The type of a variable holding a value is correlated to its value.
The type of a strongly declared empty variable is the type with which it was declared.
The type of an empty variable that wasn’t strongly declared is (Any)
To clear the value of a variable, assign Nil to it.

3.6. Scoping

Before using a variable for the first time, it needs to be declared.

3.6.1. Lexical scope

Several declarators are used in Raku. We’ve been using my, so far.

my $var = 1;

The my declarator gives the variable lexical scope. In other words, the variable will only be accessible in the same block it was declared.

A block in Raku is delimited by { }. If no block is found, the variable will be available in the whole Raku script.

{
  my Str $var = 'Text';
  say $var;   # is accessible
}
say $var;   # is not accessible, returns an error

Since a variable is only accessible in the block where it is defined, the same variable name can be used in another block.

{
  my Str $var = 'Text';
  say $var;
}
my Int $var = 123;
say $var;

3.6.2. Other declarators

Other declarators that you will come across are:

  • our gives the variable package scope. It can be accessed from outside the block it was declared in.

  • constant declares a value that cannot be changed.

constant $tau = pi * 2;
$tau = 6;   # returns an error: Cannot assign to an immutable value

3.7. Assignment vs. binding

3.7.1. Assignment

We’ve seen in the previous examples, how to assign values to variables.
Assignment is done using the = operator.

my Int $var = 123;
say $var;

We can change the value assigned to a variable:

Script
my Int $var = 123;
say $var;
$var = 999;
say $var;
Output
123
999

3.7.2. Binding

On the other hand, we cannot change the value bound to a variable.
Binding is done using the := operator.

Script
my Int $var := 123;
say $var;
$var = 999;
say $var;
Output
123
Cannot assign to an immutable value

3.7.3. Binding variables

Variables can also be bound to other variables:

Script
my $a;
my $b;
$b := $a;
$a = 7;
say $b;
$b = 8;
say $a;
Output
7
8

After $b := $a, both names refer to the same variable. A change made through one is visible through the other.
The direction matters when the variables already hold values: $b := $a makes $b refer to what $a holds, and $a := $b does the opposite.

For more info on variables, see https://docs.raku.org/language/variables

4. Strings

We have already used strings in many of the previous examples.
In this chapter, we will take a closer look at what can be done with them.

4.1. Interpolation

A string delimited by double quotes replaces the variables it contains with their values.
This is called interpolation.

my $name = 'John';
my $age = 23;
say "$name is $age years old";

Interpolation is not limited to variables. Code that is enclosed in curly braces is run, and its result is inserted in the string.

Script
my $name = 'John';
my $age = 23;
say "Next year $name will be {$age + 1} years old";
say "The name $name has {$name.chars} letters";
say 'The name $name has {$name.chars} letters';
Output
Next year John will be 24 years old
The name John has 4 letters
The name $name has {$name.chars} letters

The last string is delimited by single quotes, so nothing is interpolated.

4.2. Word lists

A list of words can be written without quotes and without commas, by enclosing it in angle brackets.

my @animals = <camel llama owl>;
say @animals;

It is a shorter way of writing:

my @animals = 'camel', 'llama', 'owl';
say @animals;
The words are separated by spaces. This notation cannot be used for a value that contains a space.

4.3. Common methods

Many methods can be called on a string, as shown in the below example:

Script
my $text = 'Raku is fun';
say $text.chars;
say $text.uc;
say $text.lc;
say $text.flip;
say $text.contains('fun');
say $text.starts-with('Raku');
say $text.ends-with('boring');
say $text.substr(0, 4);
say $text.index('is');
say '   Raku   '.trim;
Output
11
RAKU IS FUN
raku is fun
nuf si ukaR
True
True
False
Raku
5
Raku
Explanation

.chars returns the number of characters in the string.
.uc and .lc return the string in uppercase and in lowercase.
.flip returns the string reversed.
.contains() checks if the string contains another string.
.starts-with() and .ends-with() check how the string begins and how it ends.
.substr(a, b) returns b characters starting at position a.
.index() returns the position of another string inside the string.
.trim removes the whitespace at the beginning and at the end of the string.

None of these methods change the original string. To store the result back in the variable, use .= as we did with arrays.

4.4. Splitting and joining

A string can be split into a list, and a list can be joined into a string.

Script
my $sentence = 'The quick brown fox';
my @words = $sentence.words;
say @words;
say @words.elems;
say @words.join('-');

my $record = 'John,23,American';
my @fields = $record.split(',');
say @fields;
say @fields[0];

say 'Raku'.comb;
Output
[The quick brown fox]
4
The-quick-brown-fox
[John 23 American]
John
(R a k u)
Explanation

.words splits a string into its words.
.join() joins the elements of a list into a single string, using the given separator.
.split() splits a string wherever the given separator is found.
.comb splits a string into its characters.

4.5. Formatting

sprintf builds a string from a template.
The template contains placeholders that start with %. Each placeholder is replaced by one of the values that follow the template.

Script
say sprintf('%s is %d years old', 'John', 23);
say sprintf('The price is %.2f', 3.14159);
say sprintf('Ticket number %05d', 42);
Output
John is 23 years old
The price is 3.14
Ticket number 00042
Explanation

%s is replaced by a string.
%d is replaced by an integer.
%.2f is replaced by a number rounded to 2 decimal places.
%05d is replaced by an integer, padded with zeros to a width of 5 digits.

For the complete list of methods applicable to strings, see https://docs.raku.org/type/Str

5. Loops and conditions

Raku has many conditional and looping constructs.

5.1. if

The code runs only if a condition has been met; i.e., an expression evaluates to True.

my $age = 19;

if $age > 18 {
  say 'Welcome';
}

In Raku, we can invert the code and the condition.
Even if the code and the condition have been inverted, the condition is always evaluated first.

my $age = 19;

say 'Welcome' if $age > 18;

If the condition is not met, we can specify alternate blocks for execution by using:

  • else

  • elsif

# run the same code for different values of the variable
my $number-of-seats = 9;

if $number-of-seats <= 5 {
  say 'I am a sedan';
} elsif $number-of-seats <= 7 {
  say 'I am 7 seater';
} else {
  say 'I am a van';
}

5.2. unless

The negated version of an if statement can be written using unless.

The following code:

my $clean-shoes = False;

if not $clean-shoes {
  say 'Clean your shoes';
}

can be written as:

my $clean-shoes = False;

unless $clean-shoes {
  say 'Clean your shoes';
}

Negation in Raku is done using either ! or not.

unless (condition) is used instead of if not (condition).

unless cannot have an else clause.

5.3. with

with behaves like the if statement, but checks if the variable is defined.

my Int $var = 1;

with $var {
  say 'Hello';
}

If you run the code without assigning a value to the variable, nothing should happen.

my Int $var;

with $var {
  say 'Hello';
}

without is the negated version of with. You should be able to relate it to unless.

If the first with condition is not met, an alternate path can be specified using orwith.
with and orwith can be compared to if and elsif.

5.4. for

The for loop iterates over multiple values.

5.4.1. Arrays

my @array = 1, 2, 3;

for @array -> $array-item {
  say $array-item * 100;
}

Notice that we created an iteration variable $array-item and then performed the operation *100 on each array item.

If we do not create an iteration variable, each value is placed in a special variable called $_, also known as the topic variable.

my @array = 1, 2, 3;

for @array {
  say $_ * 100;
}

5.4.2. Ranges

The values can also come from a range:

for 1..5 -> $number {
  say $number;
}

Calling a method without writing anything before the dot calls it on the topic variable.
In other words, .say is a shorter way of writing $_.say

for 1..5 {
  .say;
}

5.4.3. Hashes

To iterate over a hash, we can ask for its keys and values and create two iteration variables:

my %capitals = UK => 'London', Germany => 'Berlin';

for %capitals.kv -> $country, $city {
  say "The capital of $country is $city";
}

5.5. given

given is the Raku equivalent of the switch statement in other languages, but much more powerful.

my $var = 42;

given $var {
  when 0..50 { say 'Less than or equal to 50'}
  when Int { say "is an Int" }
  when 42  { say 42 }
  default  { say "huh?" }
}

After a successful match, the matching process will stop.

Alternatively proceed will instruct Raku to continue matching even after a successful match.

my $var = 42;

given $var {
  when 0..50 { say 'Less than or equal to 50';proceed}
  when Int { say "is an Int";proceed}
  when 42  { say 42 }
  default  { say "huh?" }
}

5.6. loop

loop is another way of writing a for loop.

Actually, loop is how for loops are written in C-family programming languages.

loop (my $i = 0; $i < 5; $i++) {
  say "The current number is $i";
}

5.7. while and until

The while loop repeats a block of code as long as a condition is true.

my $countdown = 3;

while $countdown > 0 {
  say $countdown;
  $countdown--;
}
say 'Liftoff!';
Output
3
2
1
Liftoff!

until is the negated version of while. It repeats the block as long as the condition is false.
You should be able to relate it to unless.

The above example can be written as:

my $countdown = 3;

until $countdown == 0 {
  say $countdown;
  $countdown--;
}
say 'Liftoff!';
Make sure that the condition changes at some point, otherwise the loop will never stop.

5.8. next and last

next and last give us more control over a loop. They can be used in for, loop, while and until.

  • next skips the rest of the block and continues with the next value.

  • last stops the loop.

for 1..10 -> $number {
  next unless $number %% 2;
  last if $number > 8;
  say $number;
}
Output
2
4
6
8
Explanation

next unless $number %% 2 skips the numbers that are not divisible by 2
last if $number > 8 stops the loop as soon as the number is greater than 8, so 10 is never displayed.

For more info on loops and conditions, see https://docs.raku.org/language/control

6. I/O

In Raku, two of the most common input/output interfaces are the terminal and files.

6.1. Basic I/O using the terminal

6.1.1. say

say writes to the standard output. It appends a newline at the end. In other words, the following code:

say "Hello Ma'am.";
say "Hello Sir.";

will be written on 2 separate lines.

6.1.2. print

print on the other hand behaves like say but doesn’t add a new line.

Try replacing say with print and compare the results.

6.1.3. get

get is used to capture input from the terminal.

my $name;

say "Hi, what's your name?";
$name = get;

say "Dear $name welcome to Raku";

When the above code runs, the terminal will be waiting for you to input your name. Enter it and then hit [Enter]. Subsequently, it will greet you.

6.1.4. prompt

prompt is a combination of print and get.

The above example can be written like this:

my $name = prompt "Hi, what's your name? ";

say "Dear $name welcome to Raku";

6.2. Running shell commands

Two subroutines can be used to run shell commands:

  • run Runs an external command without involving a shell

  • shell Runs a command through the system shell. It is platform and shell dependent. All shell meta characters are interpreted by the shell, including pipes, redirects, environment variable substitutions and so on.

Run this if you’re on Linux/macOS
my $name = 'Neo';
run 'echo', "hello $name";
shell "ls";
Run this if you’re on Windows
shell "dir";

echo and ls are common shell keywords on Linux:
echo prints text to the terminal (the equivalent of say in Raku)
ls lists all files and folders in the current directory

dir is the equivalent of ls on Windows.

6.3. File I/O

6.3.1. slurp

slurp is used to read data from a file.

Create a text file with the following content:

datafile.txt
John 9
Johnnie 7
Jane 8
Joanna 7
my $data = slurp "datafile.txt";
say $data;

6.3.2. spurt

spurt is used to write data to a file.

my $newdata = "New scores:
Paul 10
Paulie 9
Paulo 11";

spurt "newdatafile.txt", $newdata;

After running the above code, a new file named newdatafile.txt will be created. It will contain the new scores.

6.3.3. lines

lines is used to read data from a file one line at a time.

We will read the same datafile.txt that we used with slurp.

for "datafile.txt".IO.lines -> $line {
  say "Score: $line";
}
Output
Score: John 9
Score: Johnnie 7
Score: Jane 8
Score: Joanna 7

.IO turns the name of the file into an object that represents the file.
.lines returns the lines of that file, and the for loop goes through them one by one.

6.4. Working with files and directories

6.4.1. Listing a directory

Raku can list the contents of a directory without resorting to shell commands (by using ls, for example).

say dir;                # List files and folders in the current directory
say dir "/Documents";   # List files and folders in the specified directory

6.4.2. Creating and deleting directories

In addition, you can create and delete directories.

mkdir "newfolder";
rmdir "newfolder";

mkdir creates a new directory.
rmdir deletes an empty directory and returns an error if not empty.

6.4.3. Checking a path

You can also check if a path exists; if it is a file; or a directory:

In the directory where you will be running the below script, create an empty folder folder123 and an empty raku file script123.raku

say "script123.raku".IO.e;
say "folder123".IO.e;

say "script123.raku".IO.d;
say "folder123".IO.d;

say "script123.raku".IO.f;
say "folder123".IO.f;

IO.e checks if the directory/file exists.
IO.f checks if the path is a file.
IO.d checks if the path is a directory.

Windows users can use / or \\ to define directories
C:\\rakudo\\bin
C:/rakudo/bin
For more info on I/O, see https://docs.raku.org/type/IO

7. Subroutines

In this chapter we will take a look at how to define subroutines, how to pass values to them and how to get values back.

7.1. Definition

Subroutines (also called subs or functions) are a means of packaging and reusing functionality.

A subroutine definition begins with the keyword sub. After their definition, they can be called by their handle.
Check out the below example:

sub alien-greeting {
  say "Hello earthlings";
}

alien-greeting;

The previous example showcased a subroutine that doesn’t require any input.

7.2. Signature

Subroutines can require input. That input is provided by arguments. A subroutine may define zero or more parameters. The number and type of parameters that a subroutine defines is called its signature.

The below subroutine accepts a string argument.

sub say-hello(Str $name) {
  say "Hello " ~ $name ~ "!!!!";
}
say-hello "Paul";
say-hello "Paula";

7.3. Multiple dispatch

It is possible to define multiple subroutines that have the same name but different signatures. When the subroutine is called, the runtime environment will decide which version to use based on the number and type of supplied arguments. This type of subroutine is defined the same way as normal subs except that we use the multi keyword instead of sub.

multi greet($name) {
  say "Good morning $name";
}
multi greet($name, $title) {
  say "Good morning $title $name";
}

greet "Johnnie";
greet "Laura", "Mrs.";

7.4. Default and optional parameters

If a subroutine is defined to accept an argument, and we call it without providing it with the required argument, it will fail.

Raku provides us the ability to define subroutines with:

  • Optional parameters

  • Default parameters

7.4.1. Optional parameters

Optional parameters are defined by appending ? to the parameter name.

sub say-hello($name?) {
  with $name { say "Hello " ~ $name }
  else { say "Hello Human" }
}
say-hello;
say-hello("Laura");

7.4.2. Default parameters

If the user doesn’t need to supply an argument, a default value can be defined.
This is done by assigning a value to the parameter within the subroutine definition.

sub say-hello($name = "Matt") {
  say "Hello " ~ $name;
}
say-hello;
say-hello("Laura");

7.5. Named parameters

The parameters we have used so far are called positional parameters. The arguments are matched to them by position: the first argument goes to the first parameter, the second argument to the second parameter and so on.

A named parameter is matched by its name instead. It is defined by adding a colon : before the parameter.

sub greet(:$name, :$title) {
  say "Good morning $title $name";
}

greet(name => "Laura", title => "Mrs.");
greet(title => "Mr.", name => "Johnnie");
Output
Good morning Mrs. Laura
Good morning Mr. Johnnie

Since the arguments are matched by name, they can be supplied in any order.

If a variable has the same name as the parameter, the argument can be written in a shorter way: :$name instead of name => $name

sub greet(:$name, :$title) {
  say "Good morning $title $name";
}

my $name = "Laura";
my $title = "Mrs.";
greet(name => $name, title => $title);   # regular
greet(:$name, :$title);                  # shorthand

7.6. Slurpy parameters

A subroutine can accept any number of arguments by using a slurpy parameter.
A slurpy parameter is an array preceded by an asterisk, like *@names. It collects all the remaining arguments.

sub greet-all(*@names) {
  for @names -> $name {
    say "Hello $name";
  }
}

greet-all("Paul");
greet-all("Paul", "Paula", "Laura");
Output
Hello Paul
Hello Paul
Hello Paula
Hello Laura

7.7. Constraints

We have seen that a parameter can be restricted to a type, like Str $name.
It can be restricted further by adding the keyword where followed by a condition.

sub register(Int $age where $age >= 18) {
  say "You are registered";
}

register(21);
register(15);
Output
You are registered
Constraint type check failed in binding to parameter '$age'; expected anonymous constraint to be met but got Int (15)

The first call succeeds. The second call fails because 15 is an Int but does not meet the condition.

7.8. Returning values

All the subroutines we’ve seen so far do something — they display some text on the terminal.

Sometimes, though, we execute a subroutine for its return value so we can use it later in the flow of our program.

7.8.1. Implicit return

If a function is allowed to run through its block to the end, the last statement or expression will determine the return value.

sub squared($x) {
  $x ** 2;
}
say "7 squared is equal to " ~ squared(7);

7.8.2. Explicit return

For the sake of clarity, it might be a good idea to explicitly specify what we want returned. This can be done using the return keyword.

sub squared($x) {
  return $x ** 2;
}
say "7 squared is equal to " ~ squared(7);

7.8.3. Restricting return values

In one of the previous examples, we saw how we can restrict the accepted argument to be of a certain type. The same can be done with return values.

To restrict the return value to a certain type, we use the arrow notation --> in the signature.

Indicating return type
sub squared($x --> Int) {
  return $x ** 2;
}
say "1.2 squared is equal to " ~ squared(1.2);

If we fail to provide a return value that matches the type constraint, an error will be thrown.

Type check failed for return value; expected Int but got Rat (1.44)

Not only can type constraints control the type of the return value; they can also control its definedness.

In the previous examples, we specified that the return value should be an Int.

We could also have specified that the returned Int should be strictly defined or undefined using the following signatures:
-→ Int:D and -→ Int:U

That being said, it is good practice to use those type constraints.
Below is the modified version of the previous example that uses :D to force the returned Int to be defined.

sub squared($x --> Int:D) {
  return $x ** 2;
}
say "1.2 squared is equal to " ~ squared(1.2);
For more info on subroutines and functions, see https://docs.raku.org/language/functions

8. Functional programming

In this chapter we will take a look at some of the features that facilitate functional programming.

8.1. Functions are first-class citizens

Functions/subroutines are first-class citizens:

  • They can be passed as arguments

  • They can be returned from other functions

  • They can be assigned to variables

A great example is the map function.
map is a higher order function, it can accept another function as an argument.

Script
my @array = <1 2 3 4 5>;
sub squared($x) {
  $x ** 2;
}
say map(&squared, @array);
Output
(1 4 9 16 25)
Explanation

We defined a subroutine called squared that takes an argument and multiplies that argument by itself.
Next, we used map, a higher order function, and gave it two arguments, the squared subroutine and an array.
The result is a list of the squared elements of the array.

Notice that when passing a subroutine as an argument, we need to prepend & to its name.

8.2. Anonymous functions

An anonymous function is also called a lambda.
An anonymous function is not bound to an identifier (it has no name).

8.2.1. Pointy blocks

Let’s rewrite the map example and have it use an anonymous function

my @array = <1 2 3 4 5>;
say map(-> $x {$x ** 2}, @array);

Notice that instead of declaring the squared subroutine and passing it as an argument to map, we defined it within the anonymous subroutine as -> $x {$x ** 2}.

In Raku parlance, we call this notation a pointy block

8.2.2. Shorter notations

A pointy block that takes a single argument can be written in two shorter ways.

A block written without -> and without a parameter receives its argument in the topic variable $_

my @array = <1 2 3 4 5>;
say map({ $_ ** 2 }, @array);

An asterisk used in an expression stands for the argument, and the braces are not needed.
* ** 2 is a shorter way of writing { $_ ** 2 }

my @array = <1 2 3 4 5>;
say map(* ** 2, @array);

The three notations give the same result: (1 4 9 16 25)

8.2.3. Storing a function in a variable

A pointy block may also be used to assign functions to variables:

my $squared = -> $x {
  $x ** 2;
}
say $squared(9);

8.3. Chaining

In Raku, methods can be chained, so you’re not required to pass the result of one method to another as an argument.

To illustrate: Given an array, you may need to return the unique values of the array, sorted from biggest to smallest.

Here’s a non-chained solution:

my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = reverse(sort(unique(@array)));
say @final-array;

Here, we call unique on @array, pass the result as an argument to sort, and then pass that result to reverse.

In contrast, with chained methods, the above example can be rewritten as:

my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = @array.unique.sort.reverse;
say @final-array;

You can already see that chaining methods is easier on the eye.

8.4. Feed operator

The feed operator, called pipe in some functional programming languages, further illustrates method chaining.

8.4.1. Forward feed

my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
@array ==> unique()
       ==> sort()
       ==> reverse()
       ==> my @final-array;
say @final-array;
Explanation
Start with `@array` then return a list of unique elements
                    then sort it
                    then reverse it
                    then store the result in @final-array

Note that the flow of the method calls is top-down — from first to final step.

8.4.2. Backward feed

my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array-v2 <== reverse()
                   <== sort()
                   <== unique()
                   <== @array;
say @final-array-v2;
Explanation

The backward feed is like the forward feed, but in reverse.
The flow of the method calls is bottom-up — from final to first step.

8.5. Hyper operator

The hyper operator >>. will call a method on all elements of a list and return a list of the results.

my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub is-even($var) { $var %% 2 };

say @array>>.is-prime;
say @array>>.&is-even;

Using the hyper operator we can call methods already defined in Raku, e.g. is-prime that tells us if a number is prime or not.
In addition we can define new subroutines and call them using the hyper operator. In this case we have to prepend & to the name of the method; e.g., &is-even.

This is very practical as it relieves us from writing a for loop to iterate over each value.

Raku guarantees that the order of the results is the same as that of the original list. However, there is no guarantee that Raku will actually call the methods in list order or in the same thread. So, be careful with methods that have side-effects, such as say or print.

8.6. Junctions

A junction is a logical superposition of values.

In the below example 1|2|3 is a junction.

my $var = 2;
if $var == 1|2|3 {
  say "The variable is 1 or 2 or 3";
}

The use of junctions usually triggers autothreading; the operation is carried out for each junction element, and all the results are combined into a new junction and returned.

8.7. Sequences and lazy lists

A lazy list is a list that is lazily evaluated.
Lazy evaluation delays the evaluation of an expression until required, and avoids repeating evaluations by storing results in a lookup table.

The benefits include:

  • Performance increase by avoiding needless calculations

  • The ability to construct potentially infinite data structures

  • The ability to define control flow

To build a lazy list we use the infix operator ... called the sequence operator. What it builds is called a sequence.
A sequence has initial element(s), a generator and an endpoint.

8.7.1. Simple lazy list

my $lazylist = (1 ... 10);
say $lazylist;

The initial element is 1 and the endpoint is 10. No generator was defined so the default generator is the successor (+1)
In other words this lazy list may return (if requested) the following elements (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

8.7.2. Infinite lazy list

my $lazylist = (1 ... Inf);
say $lazylist;

This list may return (if requested) any integer between 1 and infinity, in other words any integer number.

8.7.3. Deduced generator

my $lazylist = (0, 2 ... 10);
say $lazylist;

The initial elements are 0 and 2 and the endpoint is 10. No generator was defined, but using the initial elements, Raku will deduce that the generator is (+2)
This lazy list may return (if requested) the following elements (0, 2, 4, 6, 8, 10)

8.7.4. Defined generator

my $lazylist = (0, { $_ + 3 } ... 12);
say $lazylist;

In this example, we defined explicitly a generator enclosed in { }
This lazy list may return (if requested) the following elements (0, 3, 6, 9, 12)

When using an explicit generator, the endpoint must be one of the values that the generator can return.
If we reproduce the above example with the endpoint being 10 instead of 12, the sequence will never end. The generator jumps over the endpoint.
Although the sequence goes on forever, say displays its first 100 elements and ends them with ...

Alternatively you can replace 0 ... 10 with 0 ...^ * > 10
You can read it as: From 0 until the first value greater than 10 (excluding it)

This will not stop the generator
my $lazylist = (0, { $_ + 3 } ... 10);
say $lazylist;
This will stop the generator
my $lazylist = (0, { $_ + 3 } ...^ * > 10);
say $lazylist;

8.8. Closures

All code objects in Raku are closures, which means they can reference lexical variables from an outer scope.

sub generate-greeting {
  my $name = "John Doe";
  sub greeting {
    say "Good Morning $name";
  };
  return &greeting;
}
my $generated = generate-greeting;
$generated();

If you run the above code, it will display Good Morning John Doe on the terminal.
While the result is fairly simple, what is interesting about this example, is that the greeting inner subroutine was returned from the outer subroutine before being executed.

$generated has become a closure.

A closure is a special kind of object that combines two things:

  • A subroutine

  • The environment in which that subroutine was created.

The environment consists of any local variable that was in-scope at the time that the closure was created. In this case, $generated is a closure that incorporates both the greeting subroutine and the John Doe string that existed when the closure was created.

Let’s take a look at a more interesting example.

sub greeting-generator($period) {
  return sub ($name) {
    return "Good $period $name";
  }
}
my $morning = greeting-generator("Morning");
my $evening = greeting-generator("Evening");

say $morning("John");
say $evening("Jane");

In this example, we have defined a subroutine greeting-generator($period) that accepts a single argument $period and returns a new subroutine. The subroutine it returns accepts a single argument $name and returns the constructed greeting.

Basically, greeting-generator is a subroutine factory. In this example, we used greeting-generator to create two new subroutines, one that says Good Morning and one that says Good Evening.

$morning and $evening are both closures. They share the same subroutine body definition, but store different environments.
In $morning 's environment $period is Morning. In $evening 's environment $period is Evening.

9. Classes & objects

In the previous chapter, we learned how Raku facilitates functional programming.
In this chapter we will take a look at object oriented programming in Raku.

Object oriented programming is one of the widely used paradigms nowadays.
An object is a set of variables and subroutines bundled together.
The variables are called attributes and the subroutines are called methods.
Attributes define the state and methods define the behavior of an object.

A class is a template for creating objects.

In order to understand the relationship consider the below example:

There are 4 people present in a room

objects ⇒ 4 people

These 4 people are humans

class ⇒ Human

They have different names, age, sex and nationality

attributes ⇒ name, age, sex, nationality

In object oriented parlance, we say that objects are instances of a class.

9.1. Defining a class

Consider the below script:

class Human {
  has $.name;
  has $.age;
  has $.sex;
  has $.nationality;
}

my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American');
say $john;

The class keyword is used to define a class.
The has keyword is used to define attributes of a class.
The .new() method is called a constructor. It creates the object as an instance of the class it has been called on.

In the above script, a new variable $john holds a reference to a new instance of "Human" defined by Human.new().
The arguments passed to the .new() method are used to set the attributes of the underlying object.

A class can be given lexical scope using my:

my class Human {

}

9.2. Encapsulation

Encapsulation is an object oriented concept that bundles a set of data and methods together.
The data (attributes) within an object should be private, in other words, accessible only from within the object.
In order to access the attributes from outside the object, we use methods called accessors.

The below two scripts have the same result.

Direct access to the variable:
my $var = 7;
say $var;
Encapsulation:
my $var = 7;
sub sayvar {
  $var;
}
say sayvar;

The method sayvar is an accessor. It lets us access the value of the variable without getting direct access to it.

Encapsulation is facilitated in Raku with the use of twigils.
Twigils are secondary sigils. They come between the sigil and the attribute name.
Two twigils are used in classes:

  • ! is used to explicitly declare that the attribute is private.

  • . is used to automatically generate an accessor for the attribute.

By default, all attributes are private. The . twigil is what makes an attribute readable from outside the object.

To see the difference, let’s declare the attributes of our class with ! instead of .

class Human {
  has $!name;
  has $!age;
  has $!sex;
  has $!nationality;
}

my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American');
say $john;

Run it and say $john will print Human.new. The object is empty.
The default constructor only sets the attributes that have an accessor, so the values we passed to it were ignored.

Append to the script the following statement: say $john.age;
It will return this error: No such method 'age' for invocant of type 'Human' because $!age is private and can only be used within the object. Trying to access it outside the object will return an error.

Now replace has $!age with has $.age and run the script again.
say $john will print Human.new(age ⇒ 23) and say $john.age will print 23

Private attributes are set and used from within the class. We will see how in the section about methods.

9.3. Named vs. positional parameters

In Raku, all classes inherit a default .new() constructor.
It can be used to create objects by providing it with arguments.
The default constructor can only be provided with named arguments.
In our example above, notice that the arguments supplied to .new() are defined by name:

  • name => 'John'

  • age => 23

What if I do not want to supply the name of each attribute each time I want to create an object?
Then I need to create another constructor that accepts positional arguments.

class Human {
  has $.name;
  has $.age;
  has $.sex;
  has $.nationality;
  # new constructor that overrides the default one.
  method new($name, $age, $sex, $nationality) {
    self.bless(:$name, :$age, :$sex, :$nationality);
  }
}

my $john = Human.new('John', 23, 'M', 'American');
say $john;

9.4. Default and required attributes

We have seen that a subroutine fails if it is called without one of its arguments.
The default constructor behaves differently. An attribute that is not supplied to .new() is simply left empty.

Like the parameters of a subroutine, an attribute can be given a default value.
This is done by assigning a value to the attribute within the class definition.

If an attribute must always be supplied, we label it using the keywords is required

class Human {
  has $.name is required;
  has $.nationality = 'Unknown';
}

my $john = Human.new(name => 'John');
say $john.nationality;

my $nobody = Human.new;
Output
Unknown
The attribute '$!name' is required, but you did not provide a value for it.

The nationality of $john was not supplied, so the default value was used.
$nobody could not be created because a name is required.

9.5. Methods

Methods are the subroutines of an object.
Like subroutines, they are a means of packaging a set of functionality, they accept arguments, have a signature and can be defined as multi.

Methods are defined using the method keyword.
In normal circumstances, methods are required to perform some sort of action on the objects' attributes. This enforces the concept of encapsulation. Object attributes can only be manipulated from within the object using methods. The outside world can only interact with the object methods, and has no direct access to its attributes.

class Human {
  has $.name;
  has $.age;
  has $.sex;
  has $.nationality;
  has $.eligible;
  method assess-eligibility {
    if self.age < 21 {
      $!eligible = 'No';
    } else {
      $!eligible = 'Yes';
    }
  }

}

my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American');
$john.assess-eligibility;
say $john.eligible;

Once methods are defined within a class, they can be called on an object using the dot notation:
object . method or as in the above example: $john.assess-eligibility

Within the definition of a method, if we need to reference the object itself to call another method we use the self keyword.

Within the definition of a method, if we need to reference an attribute we use ! even if it was defined with .
The rationale being that what the . twigil does is declare an attribute with ! and automate the creation of an accessor.

In the above example, if self.age < 21 and if $!age < 21 would have the same effect, although they are technically different:

  • self.age calls the .age method (accessor)
    Can be written alternatively as $.age

  • $!age is a direct call to the variable

9.5.1. Private methods

Normal methods can be called on objects from outside the class.

Private methods are methods that can only be called from within the class.
A possible use case would be a method that calls another one for specific action. The method that interfaces with the outside world is public while the one referenced should stay private. We do not want users to call it directly, so we declare it as private.

The declaration of a private method requires the use of the ! twigil before its name.
Private methods are called with ! instead of .

method !iamprivate {
  # code goes in here
}

method iampublic {
  self!iamprivate;
  # do additional things
}

9.6. Class attributes

Class attributes are attributes that belong to the class itself and not to its objects.
They can be initialized during definition.
Class attributes are declared using my instead of has.
They are called on the class itself instead of its objects.

class Human {
  has $.name;
  my $.counter = 0;
  method new($name) {
    Human.counter++;
    self.bless(:$name);
  }
}
my $a = Human.new('a');
my $b = Human.new('b');

say Human.counter;

9.7. Access type

Until now, all the examples that we’ve seen have used accessors to get information from the objects' attributes.

What if we need to modify the value of an attribute?
We need to label it as read/write using the keywords is rw

class Human {
  has $.name;
  has $.age is rw;
}
my $john = Human.new(name => 'John', age => 21);
say $john.age;

$john.age = 23;
say $john.age;

By default, all attributes are declared as read only but you can explicitly do it using is readonly

9.8. Inheritance

Inheritance is another concept of object oriented programming.

When defining classes, soon enough we will realize that some attributes/methods are common to many classes.
Should we duplicate code?
NO! We should use inheritance

Let’s consider we want to define two classes, a class for human beings and a class for employees.
Human beings have 2 attributes: name and age.
Employees have 4 attributes: name, age, company and salary

One would be tempted to define the classes as:

class Human {
  has $.name;
  has $.age;
}

class Employee {
  has $.name;
  has $.age;
  has $.company;
  has $.salary;
}

While technically correct, the above piece of code is considered conceptually poor.

A better way to write this would be:

class Human {
  has $.name;
  has $.age;
}

class Employee is Human {
  has $.company;
  has $.salary;
}

The is keyword defines inheritance.
In object oriented parlance, we say Employee is a child of Human and that Human is a parent of Employee.

All child classes inherit the attributes and methods of the parent class, so there is no need to redefine them.

9.8.1. Overriding

Classes inherit all attributes and methods from their parent classes.
There are cases where we need the method in the child class to behave differently than the one inherited.
To achieve this, we redefine the method in the child class.
This concept is called overriding.

In the below example, the method introduce-yourself is inherited by the Employee class.

class Human {
  has $.name;
  has $.age;
  method introduce-yourself {
    say 'Hi I am a human being, my name is ' ~ self.name;
  }
}

class Employee is Human {
  has $.company;
  has $.salary;
}

my $john = Human.new(name =>'John', age => 23, );
my $jane = Employee.new(name =>'Jane', age => 25, company => 'Acme', salary => 4000);

$john.introduce-yourself;
$jane.introduce-yourself;

Overriding works like this:

class Human {
  has $.name;
  has $.age;
  method introduce-yourself {
    say 'Hi I am a human being, my name is ' ~ self.name;
  }
}

class Employee is Human {
  has $.company;
  has $.salary;
  method introduce-yourself {
    say 'Hi I am an employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company;
  }

}

my $john = Human.new(name =>'John', age => 23, );
my $jane = Employee.new(name =>'Jane', age => 25, company => 'Acme', salary => 4000);

$john.introduce-yourself;
$jane.introduce-yourself;

Depending on the class of the object, the right method will be called.

9.8.2. Submethods

Submethods are a type of method that are not inherited by child classes.
They are only accessible from the class they were declared in.
They are defined using the submethod keyword.

9.9. Multiple inheritance

Multiple inheritance is allowed in Raku. A class can inherit from multiple other classes.

class bar-chart {
  has Int @.bar-values;
  method plot {
    say @.bar-values;
  }
}

class line-chart {
  has Int @.line-values;
  method plot {
    say @.line-values;
  }
}

class combo-chart is bar-chart is line-chart {
}

my $actual-sales = bar-chart.new(bar-values => [10, 9, 11, 8, 7, 10]);
my $forecast-sales = line-chart.new(line-values => [9, 8, 10, 7, 6, 9]);

my $actual-vs-forecast = combo-chart.new(bar-values => [10, 9, 11, 8, 7, 10],
                                         line-values => [9, 8, 10, 7, 6, 9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Output
Actual sales:
[10 9 11 8 7 10]
Forecast sales:
[9 8 10 7 6 9]
Actual vs Forecast:
[10 9 11 8 7 10]
Explanation

The combo-chart class should be able to hold two series, one for the actual values plotted on bars, and another for forecast values plotted on a line.
This is why we defined it as a child of line-chart and bar-chart.
You should have noticed that calling the method plot on the combo-chart didn’t yield the required result. Only one series was plotted.
Why did this happen?
combo-chart inherits from line-chart and bar-chart, and both of them have a method called plot. When we call that method on combo-chart Raku internals will try to resolve the conflict by calling one of the inherited methods.

Correction

In order to behave correctly, we should have overridden the method plot in the combo-chart.

class bar-chart {
  has Int @.bar-values;
  method plot {
    say @.bar-values;
  }
}

class line-chart {
  has Int @.line-values;
  method plot {
    say @.line-values;
  }
}

class combo-chart is bar-chart is line-chart {
  method plot {
    say @.bar-values;
    say @.line-values;
  }
}

my $actual-sales = bar-chart.new(bar-values => [10, 9, 11, 8, 7, 10]);
my $forecast-sales = line-chart.new(line-values => [9, 8, 10, 7, 6, 9]);

my $actual-vs-forecast = combo-chart.new(bar-values => [10, 9, 11, 8, 7, 10],
                                         line-values => [9, 8, 10, 7, 6, 9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Output
Actual sales:
[10 9 11 8 7 10]
Forecast sales:
[9 8 10 7 6 9]
Actual vs Forecast:
[10 9 11 8 7 10]
[9 8 10 7 6 9]

9.10. Roles

Roles are similar to classes in that they are a collection of attributes and methods.

Roles are declared with the keyword role. Classes that wish to implement a role, do so using the does keyword.

Let’s rewrite the multiple inheritance example using roles:
role bar-chart {
  has Int @.bar-values;
  method plot {
    say @.bar-values;
  }
}

role line-chart {
  has Int @.line-values;
  method plot {
    say @.line-values;
  }
}

class combo-chart does bar-chart does line-chart {
  method plot {
    say @.bar-values;
    say @.line-values;
  }
}

my $actual-sales = bar-chart.new(bar-values => [10, 9, 11, 8, 7, 10]);
my $forecast-sales = line-chart.new(line-values => [9, 8, 10, 7, 6, 9]);

my $actual-vs-forecast = combo-chart.new(bar-values => [10, 9, 11, 8, 7, 10],
                                         line-values => [9, 8, 10, 7, 6, 9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;

Run the above script and you will see that results are the same.

By now you’re asking yourself: If roles behave like classes, what’s their use?
To answer your question, modify the first script used to showcase multiple inheritance, the one where we forgot to override the plot method.

role bar-chart {
  has Int @.bar-values;
  method plot {
    say @.bar-values;
  }
}

role line-chart {
  has Int @.line-values;
  method plot {
    say @.line-values;
  }
}

class combo-chart does bar-chart does line-chart {
}

my $actual-sales = bar-chart.new(bar-values => [10, 9, 11, 8, 7, 10]);
my $forecast-sales = line-chart.new(line-values => [9, 8, 10, 7, 6, 9]);

my $actual-vs-forecast = combo-chart.new(bar-values => [10, 9, 11, 8, 7, 10],
                                         line-values => [9, 8, 10, 7, 6, 9]);
say "Actual sales:";
$actual-sales.plot;
say "Forecast sales:";
$forecast-sales.plot;
say "Actual vs Forecast:";
$actual-vs-forecast.plot;
Output
===SORRY!===
Method 'plot' must be resolved by class combo-chart because it exists in multiple roles (line-chart, bar-chart)
Explanation

If multiple roles are applied to the same class and a conflict exists, a compile-time error will be thrown.
This is a much safer approach than multiple inheritance, where conflicts are not considered errors and are simply resolved at runtime.

Roles will warn you that there’s a conflict.

9.11. Introspection

Introspection is the process of getting information about an object, like its type, attributes or methods.

class Human {
  has Str $.name;
  has Int $.age;
  method introduce-yourself {
    say 'Hi I am a human being, my name is ' ~ self.name;
  }
}

class Employee is Human {
  has Str $.company;
  has Int $.salary;
  method introduce-yourself {
    say 'Hi I am an employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company;
  }
}

my $john = Human.new(name =>'John', age => 23, );
my $jane = Employee.new(name =>'Jane', age => 25, company => 'Acme', salary => 4000);

say $john.WHAT;
say $jane.WHAT;
say $john.^attributes;
say $jane.^attributes;
say $john.^methods;
say $jane.^methods;
say $jane.^parents;
if $jane ~~ Human {say 'Jane is a Human'};

Introspection is facilitated by:

  • .WHAT — returns the class from which the object was created

  • .^attributes — returns all the attributes of the object

  • .^methods — returns all the methods that can be called on the object

  • .^parents — returns the parent classes of the object

  • ~~ is called the smart-match operator. It evaluates to True if the object is created from the class it is being compared against or any of its inheritances.

For more info on object oriented programming in Raku, see:

10. Exception handling

Exceptions are a special behavior that happens at runtime when something goes wrong.
We say that exceptions are thrown.

Consider the below script that runs correctly:

my Str $name;
$name = "Joanna";
say "Hello " ~ $name;
say "How are you doing today?";
Output
Hello Joanna
How are you doing today?

Now consider this script that throws an exception:

my Str $name;
$name = 123;
say "Hello " ~ $name;
say "How are you doing today?";
Output
Type check failed in assignment to $name; expected Str but got Int
   in block <unit> at exceptions.raku:2

Notice that whenever an error occurs (in this case, assigning a number to a string variable) the program will stop and other lines of code will not be evaluated.

10.1. Catching exceptions

Exception handling is the process of catching an exception that has been thrown in order for the script to continue working.

my Str $name;
try {
  $name = 123;
  say "Hello " ~ $name;
  CATCH {
    default {
      say "Can you tell us your name again, we couldn't find it in the register.";
    }
  }
}
say "How are you doing today?";
Output
Can you tell us your name again, we couldn't find it in the register.
How are you doing today?

Exception handling is done by using a try-catch block.

try {
  # code goes in here
  # if anything goes wrong, the script will enter the below CATCH block
  # if nothing goes wrong, the CATCH block will be ignored
  CATCH {
    default {
      # the code in here will be evaluated only if an exception has been thrown
    }
  }
}

10.2. Exception types

The CATCH block can be defined the same way a given block is defined. This means we can catch and handle differently many types of exceptions.

try {
  # code goes in here
  # if anything goes wrong, the script will enter the below CATCH block
  # if nothing goes wrong, the CATCH block will be ignored
  CATCH {
    when X::AdHoc   { #`(do something if exception of type X::AdHoc is thrown) }
    when X::IO      { #`(do something if exception of type X::IO is thrown) }
    when X::OS      { #`(do something if exception of type X::OS is thrown) }
    default         { #`(do something if exception is thrown and doesn't belong to the above types) }
  }
}

All exception types descend from the class Exception. The names of the built-in ones start with X::, below are a few examples:
X::AdHoc is the simplest exception type
X::IO is related to IO errors
X::OS is related to OS errors
X::Str::Numeric related to trying to coerce a string to a number

For a complete list of exception types and their associated methods, go to https://docs.raku.org/types#Exception_classes

10.3. Exception messages

Within the CATCH block, the exception that was thrown is found in the topic variable $_
Calling .message on it returns the description of the error.

my Str $name;
try {
  $name = 123;
  say "Hello " ~ $name;
  CATCH {
    default {
      say "Something went wrong: " ~ .message;
    }
  }
}
say "How are you doing today?";
Output
Something went wrong: Type check failed in assignment to $name; expected Str but got Int (123)
How are you doing today?

try can also be used without a CATCH block. In that case, the exception is stored in the special variable $!

my Str $name;
try {
  $name = 123;
}
if $! {
  say "Something went wrong: " ~ $!.message;
}
say "How are you doing today?";
Output
Something went wrong: Type check failed in assignment to $name; expected Str but got Int (123)
How are you doing today?

If nothing goes wrong inside the try block, $! stays empty and the if block is skipped.

10.4. Throwing exceptions

Raku also allows you to explicitly throw exceptions.
Two types of exceptions can be thrown:

  • ad-hoc exceptions

  • typed exceptions

ad-hoc
my Int $age = 21;
die "Error !";
typed
my Int $age = 21;
X::AdHoc.new(payload => 'Error !').throw;

Ad-hoc exceptions are thrown using the die subroutine, followed by the exception message.

Typed exceptions are objects, hence the use of the .new() constructor in the above example.

For more info on exceptions, see https://docs.raku.org/language/exceptions

11. Regular expressions

A regular expression, or regex, is a sequence of characters that is used for pattern matching.
Think of it as a pattern.

if 'enlightenment' ~~ m/ light / {
  say "enlightenment contains the word light";
}

In this example, the smart match operator ~~ is used to check if a string (enlightenment) contains the word (light).
"Enlightenment" is matched against the regex m/ light /

11.1. Regex definition

A regular expression can be defined like this:

  • /light/

  • m/light/

  • rx/light/

Unless specified explicitly, white space is ignored; m/light/ and m/ light / are the same.

11.2. Matching characters

Alphanumeric characters and the underscore _ are written as is.
All other characters have to be escaped using a backslash or surrounded by quotes.

Backslash
if 'Temperature: 13' ~~ m/ \: / {
  say "The string provided contains a colon :";
}
Single quotes
if 'Age = 13' ~~ m/ '=' / {
  say "The string provided contains an equal character = ";
}
Double quotes
if 'name@company.com' ~~ m/ "@" / {
  say "This is a valid email address because it contains an @ character";
}

11.3. Matching categories of characters

Characters can be classified into categories and we can match against them.
We can also match against the inverse of that category (everything except it):

Category

Regex

Inverse

Regex

Word character (letter, digit or underscore)

\w

Any character except a word character

\W

Digit

\d

Any character except a digit

\D

Whitespace

\s

Any character except a whitespace

\S

Horizontal whitespace

\h

Any character except a horizontal whitespace

\H

Vertical whitespace

\v

Any character except a vertical whitespace

\V

Tab

\t

Any character except a tab

\T

New line

\n

Any character except a new line

\N

if "John123" ~~ / \d / {
  say "This is not a valid name, numbers are not allowed";
} else {
  say "This is a valid name";
}
if "John-Doe" ~~ / \s / {
  say "This string contains whitespace";
} else {
  say "This string doesn't contain whitespace";
}

11.4. Unicode properties

Matching against categories of characters, as seen in the preceding section, is convenient.
That being said, a more systematic approach would be to use Unicode properties.
This allows you to match against categories of characters inside and outside of
the ASCII standard.
Unicode properties are enclosed in <: >

if "Devanagari Numbers १२३" ~~ / <:N> / {
  say "Contains a number";
} else {
  say "Doesn't contain a number";
}
if "Привет, Иван." ~~ / <:Lu> / {
  say "Contains an uppercase letter";
} else {
  say "Doesn't contain an upper case letter";
}
if "John-Doe" ~~ / <:Pd> / {
  say "Contains a dash";
} else {
  say "Doesn't contain a dash";
}

11.5. Wildcards

Wildcards can also be used in a regex.

The dot . means any single character.

if 'abc' ~~ m/ a.c / {
  say "Match";
}
if 'a2c' ~~ m/ a.c / {
  say "Match";
}
if 'ac' ~~ m/ a.c / {
  say "Match";
} else {
  say "No Match";
}

11.6. Quantifiers

Quantifiers come after a character and are used to specify how many times we are expecting it.

11.6.1. Zero or one time

The question mark ? means zero or one time.

if 'ac' ~~ m/ a?c / {
  say "Match";
} else {
  say "No Match";
}
if 'c' ~~ m/ a?c / {
  say "Match";
} else {
  say "No Match";
}

11.6.2. Zero or multiple times

The star * means zero or multiple times.

if 'az' ~~ m/ a*z / {
  say "Match";
} else {
  say "No Match";
}
if 'aaz' ~~ m/ a*z / {
  say "Match";
} else {
  say "No Match";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
  say "Match";
} else {
  say "No Match";
}
if 'z' ~~ m/ a*z / {
  say "Match";
} else {
  say "No Match";
}

11.6.3. At least one time

The + means at least one time.

if 'az' ~~ m/ a+z / {
  say "Match";
} else {
  say "No Match";
}
if 'aaz' ~~ m/ a+z / {
  say "Match";
} else {
  say "No Match";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
  say "Match";
} else {
  say "No Match";
}
if 'z' ~~ m/ a+z / {
  say "Match";
} else {
  say "No Match";
}

11.7. Anchors

By default, a regex can match anywhere in a string. Anchors tie the match to a position in the string.

  • ^ matches the beginning of the string.

  • $ matches the end of the string.

if 'raku guide' ~~ m/ ^ raku / {
  say "Match";
} else {
  say "No Match";
}
if 'the raku guide' ~~ m/ ^ raku / {
  say "Match";
} else {
  say "No Match";
}
if 'the raku guide' ~~ m/ guide $ / {
  say "Match";
} else {
  say "No Match";
}

11.8. Alternation

The vertical bar | means or. It is used to match one of several alternatives.

if 'raku.org' ~~ m/ com | org | net / {
  say "Match";
} else {
  say "No Match";
}
if 'raku.dev' ~~ m/ com | org | net / {
  say "Match";
} else {
  say "No Match";
}

11.9. Match results

Whenever the process of matching a string against a regex is successful, the match result is stored in a special variable $/

Script
if 'I am learning Raku regexes today' ~~ m/:s Raku regexes/ {
  say "The match is: " ~ $/;
  say "The string before the match is: " ~ $/.prematch;
  say "The string after the match is: " ~ $/.postmatch;
  say "The matching string starts at position: " ~ $/.from;
  say "The matching string ends at position: " ~ $/.to;
}
Output
The match is: Raku regexes
The string before the match is: I am learning
The string after the match is: today
The matching string starts at position: 14
The matching string ends at position: 26
Explanation

$/ returns a match object (the string that matches the regex)
The following methods can be called on the match object:
.prematch returns the string preceding the match.
.postmatch returns the string following the match.
.from returns the starting position of the match.
.to returns the ending position of the match.

By default, whitespace in a regex definition is ignored.
If we want to match against a regex containing whitespace, we have to do so explicitly.
The :s in the regex m/:s Raku regexes/ forces whitespace to be considered.
Alternatively, we could have written the regex as m/ Raku\sregexes / and used \s which represents a whitespace.
If a regex contains more than a single whitespace, using :s is a better option than using \s for each and every whitespace.

11.10. Captures

Surrounding a part of a regex with parentheses captures the part of the string that it matches.
The captures are numbered starting from zero. They are stored in the variables $0, $1, $2 and so on.

Script
if 'John is 23 years old' ~~ m/ (\w+) \s is \s (\d+) / {
  say "Name: " ~ $0;
  say "Age: " ~ $1;
}
Output
Name: John
Age: 23
Explanation

(\w+) is the first capture. It matches John and stores it in $0
(\d+) is the second capture. It matches 23 and stores it in $1

11.11. Substitution

A regex can also be used to replace the part of a string that it matches.
This is done using the .subst method. It is given a regex and a replacement, and it returns a new string.

Script
my $text = 'I drink tea, tea is good';
say $text.subst(/tea/, 'coffee');
say $text.subst(/tea/, 'coffee', :g);
say $text;
Output
I drink coffee, tea is good
I drink coffee, coffee is good
I drink tea, tea is good
Explanation

.subst replaces the first match only.
Adding :g (short for global) replaces all the matches.
.subst does not change the original string, as shown by the last line.

11.12. Example

Let’s check if an email is valid or not.
For the sake of this example we will assume that a valid email address has this format:
first name [dot] last name [at] company [dot] (com/org/net)

The regex used in this example for email validation is not very accurate.
Its sole purpose is to demonstrate regex functionality in Raku.
Do not use it as-is in production.
Script
my $email = 'john.doe@route66.org';
my $regex = / ^ <:L>+\.<:L>+\@<:L+:N>+\.(com|org|net) $ /;

if $email ~~ $regex {
  say $/ ~ " is a valid email";
} else {
  say "This is not a valid email";
}
Output
john.doe@route66.org is a valid email
Explanation
  • <:L> matches a single letter

  • <:L>+ matches one or more letters

  • \. matches a single [dot] character

  • \@ matches a single [at] character

  • <:L+:N> matches a letter or a single number

  • <:L+:N>+ matches one or more letters or numbers

  • (com|org|net) matches one of the three alternatives

  • ^ and $ make sure that nothing comes before or after the email

The regex can be decomposed as following:

  • first name <:L>+

  • [dot] \.

  • last name <:L>+

  • [at] \@

  • company name <:L+:N>+

  • [dot] \.

  • com/org/net (com|org|net)

Alternatively, a regex can be broken down into multiple named regexes
my $email = 'john.doe@route66.org';
my regex many-letters { <:L>+ };
my regex dot { \. };
my regex at { \@ };
my regex many-letters-numbers { <:L+:N>+ };
my regex domain { com | org | net };

if $email ~~ / ^ <many-letters> <dot> <many-letters> <at> <many-letters-numbers> <dot> <domain> $ / {
  say $/ ~ " is a valid email";
} else {
  say "This is not a valid email";
}

A named regex is defined using the following syntax: my regex regex-name { regex definition }
A named regex can be called using the following syntax: <regex-name>

For more info on regexes, see https://docs.raku.org/language/regexes

12. Grammars

In the previous chapter, we broke down the email regex into multiple named regexes.
A grammar takes this idea further. It groups named regexes together in order to describe the format of a whole text.

Grammars are used to parse text: to check that it follows the expected format, and to extract the parts we are interested in.

12.1. Defining a grammar

A grammar is defined using the keyword grammar. It contains named regexes that are defined using the keyword token.
The token named TOP is special. It describes the text as a whole, using the other tokens.

Let’s rewrite the email example as a grammar.

grammar Email {
  token TOP        { <first-name> '.' <last-name> '@' <company> '.' <domain> }
  token first-name { <:L>+ }
  token last-name  { <:L>+ }
  token company    { <:L+:N>+ }
  token domain     { com | org | net }
}
Explanation

TOP states that an email is made of a first name, a dot, a last name, an at sign, a company, a dot and a domain.
The other tokens define what each of these parts looks like.
The dot and the at sign are surrounded by quotes because they are not alphanumeric characters.

12.2. Parsing

A grammar is used by calling its .parse() method on a string.

Script
grammar Email {
  token TOP        { <first-name> '.' <last-name> '@' <company> '.' <domain> }
  token first-name { <:L>+ }
  token last-name  { <:L>+ }
  token company    { <:L+:N>+ }
  token domain     { com | org | net }
}

my $email = 'john.doe@route66.org';

if Email.parse($email) {
  say "$email is a valid email";
} else {
  say "This is not a valid email";
}
Output
john.doe@route66.org is a valid email

.parse() succeeds only if the whole string matches TOP. This is why we did not need the anchors ^ and $ that we used in the regex.

Now replace the email with john.doe@route66.xyz and run the script again.
It will display This is not a valid email because xyz is not one of the three domains.

12.3. Extracting data

When it succeeds, .parse() returns a single match object, which we store in a scalar.
The part of the text that was matched by a token can be accessed using the name of that token, with the same angle brackets that are used with a hash.

Script
grammar Email {
  token TOP        { <first-name> '.' <last-name> '@' <company> '.' <domain> }
  token first-name { <:L>+ }
  token last-name  { <:L>+ }
  token company    { <:L+:N>+ }
  token domain     { com | org | net }
}

my $match = Email.parse('john.doe@route66.org');

say "First name: " ~ $match<first-name>;
say "Last name: " ~ $match<last-name>;
say "Company: " ~ $match<company>;
say "Domain: " ~ $match<domain>;
Output
First name: john
Last name: doe
Company: route66
Domain: org

With a single definition, the grammar both validated the email and gave us each of its parts.

For more info on grammars, see https://docs.raku.org/language/grammars

13. Raku modules

Raku is a general purpose programming language. It can be used to tackle a multitude of tasks including: text manipulation, graphics, web, databases, network protocols etc.

Reusability is a very important concept whereby programmers don’t have to reinvent the wheel each time they want to do a new task.

Raku allows the creation and redistribution of modules. Each module is a packaged set of functionality that can be reused once installed.

Zef is a module management tool that is installed along with Rakudo.

To install a specific module, type the below command in your terminal:

zef install "module name"

The Raku modules directory can be found on: https://raku.land/

13.1. Using modules

JSON is a text format for storing and exchanging data.
It is used by many web services and applications, so sooner or later your program will have to read it or produce it.
A JSON document is made of key/value pairs and lists. They map naturally to the hashes and arrays of Raku.

Luckily, you don’t need to write a JSON parser yourself; there’s a Raku module already implemented.

Let’s install it:
zef install JSON::Fast

Now, run the below script:

use JSON::Fast;

my %user = name => 'John', age => 23;
my $json = to-json(%user);

say $json;
Output
{
  "name": "John",
  "age": 23
}

In order to run the to-json() function that turns a hash into JSON, we need to load the required module.
The use keyword loads the module for use in the script which provides a to-json subroutine.

Raku does not keep the keys of a hash in a fixed order, so age might come before name when you run the script. The order can change each time the script runs.

The same module provides a from-json() subroutine that works in the opposite direction.

use JSON::Fast;

my $json = '{ "name": "Jane", "age": 25 }';
my %user = from-json($json);

say %user<name>;
say %user<age>;
Output
Jane
25
For more info on modules, see https://docs.raku.org/language/modules

14. Unicode

Unicode is a standard for encoding and representing text for most writing systems in the world.
UTF-8 is a character encoding capable of encoding all possible characters, or code points, in Unicode.

Characters are defined by a:
Grapheme: Visual representation.
Code point: A number assigned to the character.
Code point name: A name assigned to the character.

14.1. Using Unicode

14.1.1. Writing characters

Let’s look at how we can output characters using Unicode
say "a";
say "\x0061";
say "\c[LATIN SMALL LETTER A]";

The above 3 lines showcase different ways of building a character:

  1. Writing the character directly (grapheme)

  2. Using \x and the code point

  3. Using \c and the code point name

Now let’s output a smiley
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";

14.1.2. Combining characters

Another example combining two code points
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";

The letter á can be written:

  • using its unique code point \x00e1

  • or as a combination of the code points of a and acute \x0061\x0301

Some of the methods that can be used:
say "á".NFC;
say "á".NFD;
say "á".uniname;
Output
NFC:0x<00e1>
NFD:0x<0061 0301>
LATIN SMALL LETTER A WITH ACUTE

NFC returns the unique code point.
NFD decomposes the character and returns the code point of each part.
uniname returns the code point name.

14.1.3. Identifiers and math

Unicode letters can be used as identifiers:
my $Δ = 1;
$Δ++;
say $Δ;
Unicode can be used to do math:
my $var = 2 + ⅒;
say $var;

14.2. Unicode-aware operations

14.2.1. Numbers

Arabic numerals are the ten digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. This numeral set is the most used worldwide.

Nonetheless different sets of numerals are used to a lesser extent in different parts of the world.

No special care needs to be taken when using a numeral set different than the Arabic numerals. All methods/operators work as expected.

say (٤, ٥, ٦, 1, 2, 3).sort; # (1 2 3 4 5 6)
say 1 + ٩;              # 10

14.2.2. Strings

If we were to use generic string operations, we might not always get the result that we were looking for, especially when comparing or sorting.

Comparison
say 'a' cmp 'B'; # More

The above example shows that a is bigger than B. The reason being that the code point of lowercase a is bigger than the code point of capital B.

While technically correct, this is probably not what we were looking for.

Luckily Raku has methods/operators that implement the Unicode Collation Algorithm.
One of them is unicmp that behaves like the above showcased cmp but is unicode-aware.

say 'a' unicmp 'B'; # Less

As you can see, using the unicmp operator now yields the expected result that a is smaller than B.

Sorting

As an alternative to the sort method that sorts using code points, Raku provides a collate method that implements the Unicode Collation Algorithm.

say ('a', 'b', 'c', 'D', 'E', 'F').sort;    # (D E F a b c)
say ('a', 'b', 'c', 'D', 'E', 'F').collate; # (a b c D E F)
For more info on Unicode, see https://docs.raku.org/language/unicode

15. Parallelism, concurrency and asynchrony

Raku has built-in features that let a program do several things at the same time.
In this chapter we will take a look at how to run code in parallel, and how to deal with tasks that take time to complete.

15.1. Parallelism

Under normal circumstances, all tasks in a program run sequentially.
This might not be a problem, unless what you’re trying to do takes a lot of time.

Thankfully, Raku has features that will enable you to run things in parallel.
At this stage, it is important to note that parallelism can mean one of two things:

  • Task parallelism: Two (or more) independent expressions running in parallel.

  • Data parallelism: A single expression iterating over a list of elements in parallel.

Let’s begin with the latter.

15.1.1. Data parallelism

my @array = 0..50000;                       # Array population
my @result = @array.map({ is-prime $_ });   # call is-prime for each array element
say now - INIT now;                         # Output the time it took for the script to complete
Considering the above example:

We are only doing one operation @array.map({ is-prime $_ })
The is-prime subroutine is being called for each array element sequentially:
is-prime @array[0] then is-prime @array[1] then is-prime @array[2] etc.

Fortunately we can call is-prime on multiple array elements at the same time:
my @array = 0..50000;                           # Array population
my @result = @array.race.map({ is-prime $_ });  # call is-prime for each array element
say now - INIT now;                             # Output the time it took to complete

Notice the use of race in the expression. This method will enable parallel iteration of the array elements.

After running both examples (with and without race), compare the time it took for both scripts to complete.

race will not preserve the order of elements. If you wish to do so, use hyper instead.

race
my @array = 1..1000;
my @result = @array.race.map( {$_ + 1} );
.say for @result;
hyper
my @array = 1..1000;
my @result = @array.hyper.map( {$_ + 1} );
.say for @result;

If you run both examples, you should notice that one is sorted and the other is not.

15.1.2. Task parallelism

my @array1 = 0..49999;
my @array2 = 2..50001;

my @result1 = @array1.map( {is-prime($_ + 1)} );
my @result2 = @array2.map( {is-prime($_ - 1)} );

say @result1 eqv @result2;

say now - INIT now;
Considering the above example:
  1. We defined 2 arrays

  2. applied a different operation for each array and stored the results

  3. and checked if both results are the same

The script waits for @array1.map( {is-prime($_ + 1)} ) to finish
and then evaluates @array2.map( {is-prime($_ - 1)} )

Both operations applied to each array do not depend on each other.

Why not do both in parallel?
my @array1 = 0..49999;
my @array2 = 2..50001;

my $promise1 = start @array1.map( {is-prime($_ + 1)} ).eager;
my $promise2 = start @array2.map( {is-prime($_ - 1)} ).eager;

my @result1 = await $promise1;
my @result2 = await $promise2;

say @result1 eqv @result2;

say now - INIT now;
Explanation

The start subroutine evaluates the code and returns an object of type promise or shortly a promise.
If the code is evaluated correctly, the promise will be kept.
If the code throws an exception, the promise will be broken.

The await subroutine waits for a promise.
If it’s kept it will get the returned values.
If it’s broken it will get the exception thrown.

Check the time it took each script to complete.

Parallelism always adds a threading overhead. If that overhead is not offset by gains in computational speed, the script will seem slower.
This is why, using race, hyper, start and await for fairly simple scripts can actually slow them down.

15.2. Concurrency and asynchrony

In the previous section, we used parallelism to make a computation faster.
Concurrency and asynchrony answer a different need. A program often has to wait: for a timer, for a file or for a reply from the network.
Instead of doing nothing while it waits, the program can carry on with something else and come back to the task once it is ready.

15.2.1. Promises

We have already used promises with start and await.
A promise stands for a result that is not available yet, but will be at some point.

Script
my $coffee = start { sleep 2; "Coffee is ready" };
my $toast  = start { sleep 1; "Toast is ready" };

say "Preparing breakfast";
say await $toast;
say await $coffee;
Output
Preparing breakfast
Toast is ready
Coffee is ready
Explanation

sleep pauses for the given number of seconds. We use it here to imitate a task that takes time.
start launches a task and returns a promise immediately. It does not wait for the task to finish. This is why Preparing breakfast is displayed first.
await waits for a promise and returns its result.
Both tasks run at the same time, so the script takes about 2 seconds instead of 3.

15.2.2. Supplies

A promise delivers a single result. A supply delivers many values, one after the other, over a period of time.

Supply.interval(1) is a supply that produces a new number every second: 0, 1, 2 and so on.
To act on each value as it arrives, we use react and whenever.

Script
react {
  whenever Supply.interval(1) -> $second {
    say "Tick $second";
    done if $second == 3;
  }
}
say "Finished";
Output
Tick 0
Tick 1
Tick 2
Tick 3
Finished
Explanation

whenever runs its block each time the supply produces a value.
react keeps the script waiting for new values until done is called.
done if $second == 3 stops the react block after the fourth value. Without it, the script would never end.

A react block can contain more than one whenever. This allows a script to wait for several things at once.

Script
react {
  whenever Supply.interval(1) -> $second {
    say "Tick $second";
  }
  whenever Promise.in(3.5) {
    say "Time is up";
    done;
  }
}
Output
Tick 0
Tick 1
Tick 2
Tick 3
Time is up
Explanation

Promise.in(3.5) is a promise that is kept after 3.5 seconds.
The first whenever displays a line every second.
The second whenever waits for the promise, and then stops the react block.

For more info on concurrency and asynchronous programming, see https://docs.raku.org/language/concurrency

16. Native calling interface

Raku gives us the ability to use C libraries, using the native calling interface.

NativeCall is a standard module that ships with Raku and offers a set of functionality to ease the job of interfacing Raku and C.

16.1. Calling a function

16.1.1. The C library

Consider the below C code that defines a function called hellofromc. This function prints on the terminal Hello from C. It doesn’t accept any argument nor return any value.

ncitest.c
#include <stdio.h>

void hellofromc () {
  printf("Hello from C\n");
}

16.1.2. Compiling the library

Depending on your OS run the following commands to compile the above C code into a library.

On Linux:
gcc -c -fpic ncitest.c
gcc -shared -o libncitest.so ncitest.o
On Windows:
gcc -c ncitest.c
gcc -shared -o ncitest.dll ncitest.o
On macOS:
gcc -dynamiclib -o libncitest.dylib ncitest.c

16.1.3. The Raku script

Within the same directory where you compiled your C library, create a new Raku file that contains the below code and run it.

ncitest.raku
use NativeCall;

constant LIBPATH = "$*CWD/ncitest";
sub hellofromc() is native(LIBPATH) { * }

hellofromc();
Explanation

First of all we declared that we will be using the NativeCall module.
Then we created a constant LIBPATH that holds the path to the C library.
Notice that $*CWD returns the current working directory.
Then we created a new Raku subroutine called hellofromc() that should act as a wrapper to its counterpart C function having the same name and residing in the C library found in LIBPATH.
All of this was done by using the is native trait.
Finally we called our Raku subroutine.

In essence, it all boils down to declaring a subroutine with the trait is native and the name of the C library.

16.2. Renaming a function

In the above part, we saw how we can call a very simple C function by wrapping it with a Raku subroutine having the same name, using the is native trait.

In some cases, we would want to change the name of the Raku subroutine.
To do so, we use the is symbol trait.

Let’s modify the above Raku script and rename the Raku subroutine hello instead of hellofromc

ncitest.raku
use NativeCall;

constant LIBPATH = "$*CWD/ncitest";
sub hello() is native(LIBPATH) is symbol('hellofromc') { * }

hello();
Explanation

In case the Raku subroutine has a different name than its C counterpart, we should use the is symbol trait with the name of the original C function.

16.3. Passing arguments

Compile the following modified C library and run the Raku script found below again.
Notice how we modified both C and Raku code to accept a string (char* in C and Str in Raku)

ncitest.c
#include <stdio.h>

void hellofromc (char* name) {
  printf("Hello, %s! This is C!\n", name);
}
ncitest.raku
use NativeCall;

constant LIBPATH = "$*CWD/ncitest";
sub hello(Str) is native(LIBPATH) is symbol('hellofromc') { * }

hello('Jane');

16.4. Returning values

Let’s repeat the process one more time and create a simple calculator that takes 2 integers and adds them.
Compile the C library and run the Raku script.

ncitest.c
int add (int a, int b) {
  return (a + b);
}
ncitest.raku
use NativeCall;

constant LIBPATH = "$*CWD/ncitest";
sub add(int32, int32 --> int32) is native(LIBPATH) { * }

say add(2, 3);

Notice how both C and Raku functions accept two integers and return one (int in C and int32 in Raku)

16.5. Types

You might have asked yourself why we used int32 instead of Int in the latest Raku script.
Some Raku types like Int, Rat etc. can’t be used as is to pass and receive values from a C function.
One must use in Raku the same types as the ones in C.

Luckily Raku provides many types that map to their respective C counterpart.

C type Raku type

char

int8

int8_t

short

int16

int16_t

int

int32

int32_t

int64_t

int64

unsigned char

uint8

uint8_t

unsigned short

uint16

uint16_t

unsigned int

uint32

uint32_t

uint64_t

uint64

long

long

long long

longlong

float

num32

double

num64

size_t

size_t

bool

bool

char* (String)

Str

Arrays: For example int* (Array of int) and double* (Array of double)

CArray: For example CArray[int32] and CArray[num64]

For more info on the native calling interface, see https://docs.raku.org/language/nativecall

17. The community

  • #raku IRC channel. Much discussion happens on IRC.
    This should be your go to place for any enquiry for which you want an immediate answer.

  • StackOverflow Raku questions is a place where questions about Raku can be answered more in-depth.

  • Raku Weekly a weekly overview of changes in and around Raku.

  • Planet Raku blog aggregator. Stay tuned by reading blog posts that focus on Raku.

  • /r/rakulang subscribe to the Raku subreddit.

  • @raku_news follow the community on X.

  • @rakulang follow the community on Mastodon.

  • Raku Foundation oversight body of the Raku Programming Language


1. Notations for intervals: https://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals