Raku by Example: Subroutines

Usually known as function in other languages, a subroutine (or sub for short) helps to organize and structure code. There's not distinction between a sub that returns a value and one that doesn't; they're both considered subroutines.

Sub declaration

sub shout-hello {
    say "Helloooo!"; 
}

shout-hello;   # calling the sub
shout-hello(); # same as above

sub greet {
    return 'Greeting from here!";
}

greet();       # returned value in `sink` context so it's lost
say greet();   # returned value being used

# The last expression of sub is returned implicitly.
# Thus the keyword `return` can be omitted which helps
# to simplify subs with a single expression. The semicolon
# can as well be omitted in the last expression.

sub farewell {
    say "See you 'till later!"
}

farewell();
$ perl6 sub-declaration.p6
Helloooo!
Helloooo!
Greeting from here!
See you 'till later!

Positional and named parameters

  • Positional parameters are defined by their position. They're are required by default but can be specified as optional by using a default value or appending a ? to their names.

  • Named parameters are defined by their names. They're created by prepending a : to their names. Although optional by default, they can be made required by appending a ! to their names.

# 2nd parameter with default value. Required
# parameter must come first.
sub pos-fullname( $fname, $lname = "Doe" ) {
	return "$fname $lname"
}

say pos-fullname("Joe");
say pos-fullname("Tabia", "Lock");

# 1st parameter with default value
sub named-fullname( :$firstname = "Joe", :$lastname ) {
	return "$firstname $lastname"
}

say named-fullname( lastname => "Dreia" );
say named-fullname( firstname => "Cedra", lastname => "Etian" );

# Mixing positional and named parameters.
# Positional must always come first.
sub t-fullname( $title, :$firstname, :$lastname) {
	return $title ~ ". $firstname $lastname"
}

say t-fullname("Mr", firstname => "Sachio", lastname => "Eldos");
$ perl6 parameters.p6
Joe Doe
Tabia Lock
Joe Dreia
Cedra Etian
Mr. Sachio Eldos

Typed parameters

The list of parameters of a subroutine can be typed and further constraint can be specified by the definedness of the passed values. To accept only defined values of a certain type, append :D to the type. On the other hand, to accept only undefined values, append :U.

sub display-value( Str $v ) {
    say $v;
}

display-value('Hello');
display-value(Str);
#display-value(5);                 # Error


sub display-defined-value( Str:D $v ) {
    say $v;
}

display-defined-value('Hello');
#display-defined-value(Str);        # Error


sub display-undefined-value( Str:U $v ) {
    say $v;
}

display-undefined-value(Str);
#display-undefined-value('Hello');  # Error
$ perl6 typed-parameters.p6
Hello
(Str)
Hello
(Str)

Typed returns

The return type of a subroutine can be specified by using --> followed by either the type or literal value right after the parameters list.

sub multiply( $f1, $f2 --> Int ) {
	$f1 * $f2
}

say multiply(5, 3);
#say multiply(2,5, 3); # Error

# Both typed parameters and typed return
sub sum( Int $s1, Int $s2 --> Int ) {
	$s1 + $s2
}

say sum(23, 6);
#say sum(15.2, 1.7);   # Error
$ perl6 typed-returns.p6
15
29

Signatures and captures

A signature refers to the number and type of parameters defined by a routine. On the hand, a capture refers to the list of arguments a signature accepts. Thus, when a routine is defined we talk about its signature and when it's called, then we talk about its capture.

sub display( Str $person, Int $age ) {
		  #^^^^^^^^^^^^^^^^^^^^^^^^^ <- the sub's signature
	
	say "Person: $person, Age: $age";
}

display("Andrew Heid", 50);
      #^^^^^^^^^^^^^^^^^^^  <- the sub's capture

Literal signatures are created by prepending a : to a list of parameters and literal captures by prefixing a list of terms with \.

my $sig = :(Str $person, Int $age);
say ('Laurent Nga', 25) ~~ $sig;    # smartmatching against the signature

my $cap = \('Laura Sua', 27);
say $cap ~~ $sig;                   # smartmatching the capture against
									# the signature


# To use a capture in a subroutine call, prepend it with a `|`. This would
# be like passing the values directly to the sub.

display(|$cap);
$ perl6 sig-cap.p6
Person: Andrew Heid, Age: 50
True
True
Person: Laura Sua, Age: 27

Anonymous subs

An anonymous subroutine is a sub without a name. It's created by storing a sub with its parameter list and body in a variable.

my $add = sub ( $x , $y) { $x + $y };

say "3 + 7 = ", $add(3, 7);
$ perl6 anon-sub01.p6
3 + 7 = 10

A regular sub can be stored in a variable. As result, the sub can be called by its name and its handle(s). To forbid calling the sub by its name, use the anon keyword.

my $m1 = sub multiply ($a, $b) { $a * $b }
my $m2 = $m1;

say "1 x -9 = ",   multiply(-1, -9);
say "2 x 7 = ",    $m1(2, 7);
say "(i)(2+i) = ", $m2(i, 2+i);

# A regular sub can be stored in a variable by
# appending a `&` to the sub's name.

sub subtract($a, $b) { $a - $b }
my $s1 = &subtract;

say "1 - 5 = ", subtract(1,5);
say "5 - 2 = ", $s1(5,2);

# Using `anon`:
my $greet = anon sub greet(Str $name) { say "Hello, $name!" }

$greet("Jules");
#greet("Erl");    # Error: Undeclared subroutine
$ perl6 anon-sub02.p6
1 x -9 = 9
2 x 7 = 14
(i)(2+i) = -1+2i
1 - 5 = -4
5 - 2 = 3
Hello, Jules!

Instead of anonymous subs, you can utilize anonymous block of codes called pointy blocks. They are declared with an arrow -> and their parameters lists must be listed without parentheses.

my $name-n-times = -> Str $name, Int $times { say $name for 1..$times }

$name-n-times('Al', 5);

# The arrow can be dropped and instead use placeholder parameters
# with the `^$` twigil.

my $print-it = { say "$^a: $^b" }
# Equivalent to `my $print-it = -> $a, $b { say "$a: $b" }`

$print-it("Number", 15);

my $print-it-reverse = { say "$^b: $^a" }
# Equivalent to `my $print-it = -> $a, $b { say "$b: $a" }`

$print-it-reverse("Number", 15);
$ perl6 anon-sub03.p6
Al
Al
Al
Al
Al
Number: 15
15: Number

For more info, go to:

Back to main