Raku Programming/Subroutines

Subroutines

edit

When it comes to code reuse, the most basic building block is the subroutine. They are not the only building blocks in the toolkit however: Raku also supports methods and submethods, that we'll discuss when we talk about classes and objects.

Subroutines are created with the sub keyword, followed by name, an optional list of parameters, and then a block of code.

Blocks

edit

Blocks are groups of code contained in { } curly brackets. Blocks serve a number of purposes, including setting code apart, grouping several statements together, and creating a scope for variables.

Defining Subroutines

edit

Subroutines are defined using the sub keyword. Here is an example:

sub mySubroutine () {
}

The parenthesis are used to define the list of formal parameters to the subroutine. Parameters are like regular my local variables, except they are initialized with values when the subroutine is called. Subroutines can pass a result back to their caller using the return keyword:

sub double ($x) {
    my $y = $x * 2;
    return $y;
}

Optional Parameters

edit

Optional parameters have a ? after them. Also, optional parameters may be given a default value with =. Required parameters may have a ! after them, although this is the default for positional parameters. All required parameters must be listed before all optional ones.

sub foo (
    $first,       # First parameter, required
    $second!,     # Second parameter, required
    $third?,      # Third parameter, optional (defaults to undef)
    $fourth = 4   # Fourth parameter, optional (defaults to 4)
)

Named Parameters

edit

Normal parameters are passed by their position: The first passed parameter goes into the first positional argument, the second goes into the second, and so on. However, there is also a way to pass parameters by name, and to do so in any order. Named parameters are basically pairs, where a string name is associated with a data value. Named data values can be passed using either pair or adverb syntax.

sub mySub(:name($value), :othername($othervalue))

Of course, subroutine signatures allow a special shorthand, that you can use if your variable has the same name as the pair has:

sub mySub(:name($name), :othername($othername))

sub mySub(:$name, :$othername)      # Same!

In a subroutine declaration, named parameters must come after all required and optional positional parameters. Named parameters are treated as optional by default unless they are followed by a !. Actually, you can put a ! after required positional parameters as well, but that's the default.

sub mySub(
    :$name!,             # Required
    :$type,              # Optional
    :$method?            # Still optional
)

Slurpy Parameters

edit

Raku also allows so called "slurpy" parameters using the *@ syntax.

sub mySub($scalar, @array, *@theRest) {
  say "the first argument was: $scalar";
  say "the second argument was: " ~ @array;
  say "the rest were: " ~ @theRest;
}

The *@ tells Raku to flatten out the rest of the arguments into a list and store in the array @theRest. This is necessary to allow perl to accept positional or named arrays without requiring references.

  my $first = "scalar";
  my @array = 1, 2, 3;
  mySub($first, @array, "foo", "bar");

The above code will output three lines:

  • the first argument was: scalar
  • the second argument was: 1, 2, 3
  • the rest were: "foo", "bar"

return and want

edit

Calling Subroutines

edit

Once we have a subroutine defined, we can call into it later to retrieve results or actions from it. We've already seen the built-in say function, where you can pass strings to it, and have those strings printed to the console. We can use our double function from above to calculate various values:

my $x = double(2);       # 4
my $y = double(3);       # 6
my $z = double(3.5);     # 7

We can use the & sigil to store a reference to the subroutine into a normal scalar variable:

my $sub = &double;
my $x = $sub(7)          # 14

Multi Subroutines

edit

In this example, you see that we are passing both integer values and floating point values to our double subroutine. However, we can use our type specifiers to restrict what kinds of values

sub double (Int $x) {    # $x can only be an int!
   return $x * 2;
}

my $foo = double(4);     # 8
my $bar = double(1.5);   # Error!

Raku allows you to write multiple functions with the same name, so long as they have different parameter signatures and are marked with the key word multi. This is called multi method dispatch, and is an important aspect of Raku programming.

multi sub double(Int $x) {
    my $y = $x * 2;
    say "Doubling an Integer $x: $y";
    return $x * 2;
}

multi sub double(Num $x) {
    my $y = $x * 2;
    say "Doubling a Number $x: $y";
    return $x * 2;
}

my $foo = double(5);        # Doubling an Integer 5: 10
my $bar = double(3.5);      # Doubling a Number 3.5: 7

Anonymous Subroutines

edit

Instead of naming a subroutine like normal, we can define an anonymous subroutine and store a reference to it in a variable.

my $double = sub ($x) { return $x * 2; };
my $triple = sub ($x) { return $x * 3; };

my $foo = $double(5);     # 10
my $bar = $triple(12);    # 36

Notice that we could also store these code references in an array:

my @times;
@times[2] = sub ($x) { return $x * 2; };
@times[3] = sub ($x) { return $x * 3; };

my $foo = @times[2](7);     # 14
my $bar = @times[3](5);     # 15