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
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
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 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)
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.
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.
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:9as arguments in Perl 6 are by default read only;
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.
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
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)
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
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
Prints:
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.
Then we can use
that will check if 10 and 4 are in the range 2-3 .. 2+3 also known as -1 .. 5.
Published on 2012-09-05
use v6;
sub infix:<.oO>($name, $thought) {
say "$name thinks $thought"
}
'Foo' .oO 'awesome';
Foo thinks awesome
use v6;
sub infix:<+->($a, $b) { ($a-$b) .. ($a+$b) }
say 10 ~~ 2 +- 3;
say 4 ~~ 2 +- 3;
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:
This is a newsletter temporarily running on my personal site (szabgab.com) using Mailman,
till I implement an alternative system in Perl 6.
Written by Gabor Szabo
Comments
In the comments, please wrap your code snippets within <pre> </pre> tags and use spaces for indentation.
comments powered by Disqus