Camelia

Perl 6 subroutines and home made operators

This time together with a Perl 6 screencast to show the thought operator and the +- operator of Perl 6.

Direct link to the Perl 6 subroutines screencast

Note! This site is about Perl 6, the future version of Perl.
If you are looking for a solution for the current production version of Perl 5, please check out the Perl 5 tutorial.

The Thought operator

I just tried the thought operator in Perl 6. Typed in this:

"szabgab" .oO "Perl 6 is cool";

and got the following:

szabgab thinks Perl 6 is cool

The +- operator

I also tried the +- operator:

2.78  ~~ 10 +- 3 ?? 't' !! 'f'     # f
7.5   ~~ 10 +- 3 ?? 't' !! 'f'     # t
13    ~~ 10 +- 3 ?? 't' !! 'f'     # t
13.1  ~~ 10 +- 3 ?? 't' !! 'f'     # f

I think you get the point here. We check if the value on the left is between 10 +- 3.

~~ is the smart match operator, ?? !! are the two parts of the ternary operator of Perl 6 and +- is, well not everyone has the +- or the thought operator but if you read on you'll see how you can create one for yourself.

Subroutines in Perl 6

Subroutines in Perl 6 are declared using the sub keyword just as in Perl 5 But in Perl 6, after the name of the subroutine one can provide a signature, describing the parameters that function accepts.

use v6;

sub f($a, $b) {
    return "$a - $b";
}

say f(4, 2);

If called with a different number of arguments then Perl will issue an error message:

say f(4, 2, 5);

gives this message:

===SORRY!===
CHECK FAILED:
Calling 'f' will never work with argument types (Int, Int, Int) (line 7)
    Expected: :($a, $b)

or

say f(4);

gives this message:

===SORRY!===
CHECK FAILED:
Calling 'f' will never work with argument types (int) (line 7)
    Expected: :($a, $b)

Perl 5 style in Perl 6

For people who really want the Perl 5 style subroutine declaration, they can use this code:

use v6;

sub f {
    return @_.perl
}

say f(4, 2);     # Array.new(4, 2)
say f(4);        # Array.new(4)
say f(4, 2, 3);  # Array.new(4, 2, 3)

That could work for backword compability, but that would eliminate all the nice features of Perl 6.

Optional parameter

What if we would like to allow the user to pass 1 or 2 parameters? For that case we can mark the second parameter as optional using a quesion mark ?:

use v6;

sub f($a, $b?) {
    return defined($b) ?? "$a - $b" !! "$a - na";
}

say f(4, 2);    # 4 - 2
say f(4);       # 4 - na

The ??, !! pair is a the ternary operator of Perl 6. What we see above is that if $b is defined the $a - $b is returned and when $b is not defined then the word na will be returned instead of that value.

Parameters are read-only

In other cases you might be tempted to replace $b with some default value like this:

use v6;

sub f($a, $b?) {
    $b //= 17;
    return defined($b) ?? "$a - $b" !! "$a - na";
}

say f(4, 2);
say f(4);

but this will throw an exception like this:

4 - 2
Cannot assign to a readonly variable or a value
  in sub METAOP_TEST_ASSIGN:<//> at src/gen/CORE.setting:11782
  in sub f at a.pl:4
  in block  at a.pl:9
as arguments in Perl 6 are by default read only;

Default value of a parameter

There are two solutions to this, depending on what do you really want to achieve:

If you only want to give a default value to $b then the best way is to add it right in the signature:

use v6;

sub f($a, $b = 17) {
    return defined($b) ?? "$a - $b" !! "$a - na";
}

say f(4, 2);    # 4 - 2
say f(4);       # 4 - 17

In this case you don't even need the question mark '?' as the presence of a default value automatically makes the parameter optional. This still keeps $b read-only within the subroutine.

Making the parameter a copy and thus changeable

The other solution is to mark the $b variable as a copy of the original value. Therefore allowing the user to make changes to it:

use v6;

sub f($a, $b? is copy) {
    $b //= 17;
    return defined($b) ?? "$a - $b" !! "$a - na";
}

say f(4, 2);    # 4 - 2
say f(4);       # 4 - 17

Type restrictions on parameters

We can also restrict the parameters of a subroutine to certain data types:

use v6;

sub f($a, Int $b) {
    return "$a - $b";
}

my $name = "foo";
say f(4, 2);
say f(4, $name);

The first call will succeed but the second will generate an exception:

4 - 2
Nominal type check failed for parameter '$b'; expected Int but got Str instead
  in sub f at example.pl:3
  in block  at example.pl:9

If, instead of a variable, we were trying to pass a literal string "foo" as in this case:

use v6;

sub f($a, Int $b) {
    return "$a - $b";
}

say f(4, 2);
say f(4, "foo");

We would already get a compile time error:

===SORRY!===
CHECK FAILED:
Calling 'f' will never work with argument types (Int, Str) (line 8)
    Expected: :($a, Int $b)

Other constraints on parameters

It is also possible to provide further constraints on the parameters:

use v6;

sub f($a, Int $b where { $b < 10 }) {
    return "$a - $b";
}

say f(4, 2);
say f(4, 11);

The first expression is evaluated but the second one throws an exception as the condition was not fulfilled.

4 - 2
Constraint type check failed for parameter '$b'
  in sub f at a.pl:3
  in block  at a.pl:8

Creating operators in Perl 6

There are of course a lot of other aspects for subroutine definition in Perl 6 but let's now go back to our original goal, to create the +- operator and the thought operator .oO.

How can one create a new operator in Perl 6?

It is quite simple as an operator is just a subroutine with a funny way of calling it.

Here is how to create the thought operator. We create a subroutine, declaring it to be an infix,/hl> operator with the name .oO

use v6;

sub infix:<.oO>($name, $thought) {
    say "$name thinks $thought"
}

'Foo' .oO 'awesome';

Prints:

Foo thinks awesome

In the other case we want to make +- an infix operator that creates a Range. Not something complex. Within the angle brackets we put the new operator, the sub has a signature of two scalars and within the block we put actual code that needs to be executed.

use v6;

sub infix:<+->($a, $b) { ($a-$b) .. ($a+$b) }

Then we can use

say 10 ~~ 2 +- 3;
say 4 ~~ 2 +- 3;

that will check if 10 and 4 are in the range 2-3 .. 2+3 also known as -1 .. 5.


The Perl 6 Tricks and Treats newsletter has been around for a while. If you are interested to get special notification when there is new content on this site, it is the best way to keep track:
Email:
Full name:
This is a newsletter temporarily running on my personal site (szabgab.com) using Mailman, till I implement an alternative system in Perl 6.
Gabor Szabo
Written by Gabor Szabo

Published on 2012-09-05



Comments

In the comments, please wrap your code snippets within <pre> </pre> tags and use spaces for indentation.
comments powered by Disqus

Perl 6 Tricks and Treats newsletter

Register to the free newsletter now, and get updates and news.
Email:
Name: