An Overview of Raku
What is Raku and What are its Features?
Raku (also known as Perl 6) is an interpreted language designed to accompany (rather than replace) the much older Perl 5. The specification has been in the works for nearly two decades, but the first releases only started being published less than 5 years ago. There have been some salient improvements in Raku that we’ll take a look at
Static typing
Raku allows the user to declare static types if they choose. Listed below are all the common types Raku offers
my Int $favnum = 5;
my Bool $happy = true;
my Num \e = 2.71828;
my Real $dec = 1.33;
my Str $greeting = "How are you doing today?";
my Date $independence = Date.new(1776, 7, 4);
The backslash sigil for the variable e makes it immutable as the natural number’s value doesn’t have any reason to change. Static typing is totally optional; dynamic typing still works exactly as you’d expect
my $sum = 50 + 17;
With both kinds of typing at our disposal, this endows Raku with the ability to do gradual typing. This allows people to have a blend of static and dynamic typing in the same program.
Object Oriented Programming
Perl 5 has a working, but rudimentary functionality for OO. Raku significantly expands OO capabilities relative to its sister language; it is so well-revered in the Perl community that they backported some of the Raku OO features to Perl 5. Making a class is a straightforward affair and below is an basic example of one in Raku
The is rw
bit at the end of each attribute of the class ensures that they are mutable. I made my attributes pubic with the ‘.’ twigil, but using an
exclamation point will make attributes private.
Roles
Roles are the equivalent of interfaces in other languages in that they allow for one to describe the behavior of an object. They’re typically used in conjunction with classes and other objects. An example of a role might be
Concurrency
At the heart of concurrency is the use of Promises. They are used to handle events that haven’t yet been evaluated. These can be instantiated with
either the core methods available to the class or by creating your own and handling them with the vow
method:
The other primary component of concurrent programming within Raku is the class Supply. It allows us to make asynchronous data streams in a thread safe manner. One of it’s most salient use cases is in event driven programming (primarily useful in the development of GUI applications for handling a variety of user inputs). When making use of Supply, we use the tap method to subscribe to the stream and emit to specify what their output is. There are two kinds of Supplies
- live
- on-demand
Live is as one would expect; taps on the stream cannot see previously emitted values. With on-demand, taps will see all values. For example, to create a basic stream that gives the subscribers values from 1 to 10, we’d type
This short script prints the integers 1 through 10 all at once and exits. The same can be done with an on-demand supply:
Functional Programming
Raku draws some inspiration from Haskell and as such, includes some influences of functional programming; this includes support for things like lazy evaluation.
Raku’s functions are called subroutines just like in Perl. Subroutines have been given a few new notable abilities, such as the ability to be called from other subroutines, being assigned to variables, and being passed as arguments. They can also be anonymously declared; this is most often done in cases where we’re passing it as an argument.
Raku comes with a pipe functionality via the Feed Operator. This allows one to feed a program some data and then make a sequence of actions that acts on that data (i.e method calls). Similarly, Raku also has the ability to chain methods together by appending one after the other.
Grammars
The best way to think about Grammars is that what classes are to methods, grammars are to regular expressions. I think it would be appropriate to take a cursory overview of regex in Raku before talking more about Grammars.
Basic usage of regex in Raku is typically through anonymous regex, which may be constructed using with the syntax /pattern/
. The full breadth of
features provided by anonymous regular expressions begets that it be instantiated with rx/pattern/
. This form allows for the use of curly braces for
delimiters and the usage of regex adverbs, which pass along to the compiler specifiers about how the pattern needs to be interpreted, outside of the
pattern itself.
With basic regex usage out of the way, we can finally talk about Grammars. Their syntax is essentially equivalent to classes; they’re invoked via the
grammar
keyword followed by the name of the grammar and the content inside it. We don’t use methods here obviously, but the analogous use here would be
the 3 primary regex handlers: regex, rule, and token. Most of the time, you’ll use the latter two as they’re much faster than regex.
These establish regex patterns that are applied on strings that are processed in the grammar, and if there is a match, the result is stored within a
dedicate match object. We can pass strings to grammars with the parse method. Below is a rudimentary example that creates a grammar and parses a
string
File IO
Raku makes file IO incredibly easy. I can open a file and append it in a single line
spurt "example.txt", "This is sample input for this file\n", :append;
Reading from it is also trivial
my $file = 'example.txt';
say $file.IO.slurp;
With respect to handling basic file operations, Raku provides the move, rename, copy, and unlink routines; the first three are completely self-explanatory and unlink lets us remove links to files like symbolic links. We can also make and destroy directories These can be invoked procedurally as shown
move $file, '/home/user/Documents';
rename $file, 'cool-stuff';
copy $file0, $file1;
unlink $file_l;
mkdir "mydir";
rmdir "mydir";
These operations and numerous others are provided by the IO role. The methods in the various IO classes allow us to do what we might otherwise do with
shell. In the event that we want to make use of a shell command, we invoke shell(insertshellcommandhere)
. There are also methods for most of the bash
flags used often on files, such as e for exists, d for directory, s for size, etc. IO even provides methods to check access and modification times of
a file
say "/home/user/file.txt".IO.modified.DateTime;
say "/home/user/file.txt".IO.accessed.DateTime;
say "/home/user/file.txt".IO.changed.DateTime;
Debugging
The module Grammar::Tracer provides an excellent debugging facility for Grammars as there aren’t any tools in the standard library that make it easy. We install it with the zef module manager
zef install Grammar::Tracer
If I add Grammar::Tracer to the top of my initial script I used in the Grammars section, my output is
For other purposes, trace
and the Dumper function dd are quite useful. Trace prints out what lines are executed sequentially and dd shows the type of the variable and well as it’s
inputs via the stderr stream.
The Toolchain
The reference environment for Raku is Rakudo Star. It contains the zef module manager, and MoarVM or JVM backend. There are three primary means of obtaining it
- Downloading it via your Linux distribution’s package manager
- Grabbing the source and compiling it
- rakubrew (my preferred method)
To install rakubrew, we run the following
curl https://rakubrew.org/install-on-perl.sh | bash
echo 'eval "$(/home/max/.rakubrew/bin/rakubrew init bash)"' >> ~/.bashrc
After installation, all of the tools should be in $PATH and you’re all set!