Today, a small excursus into the syntax. Did you know that roles in Perl 6 can have a parameter that makes them similar to generic templates in, say, C++? Here’s a small example:
role R { has $.value; method add($b) { $.value + $b.value } method div($b) { $.value / $b.value } }
The R role defines an interface that has a value and two methods for arithmetical operations: add and div.
Now, create a class using the role, initialise two variables and use the methods to get the results:
class C does R {}
my C $x = C.new(value => 10);
my C $y = C.new(value => 3);
say $x.add($y); # 13
say $x.div($y); # 3.333333
Although the values here were integers, Perl did a good job and returned a rational number for the division. You can easily see it by calling the WHAT method:
say $x.add($y).WHAT; # (Int) say $x.div($y).WHAT; # (Rat)
If you have two integers, the result of their division is always of the Rat type. The actual operator, which is triggered in this case, is the one from src/core/Rat.pm:
multi sub infix:</>(Int \a, Int \b) { Â Â DIVIDE_NUMBERS a, b, a, b }
The DIVIDE_NUMBERS sub returns a Rat value.
Defining a role
How to modify the C class so that it performs integer division? One of the options is to use a parameterised role:
role R[::T] { has T $.value; method add($b) { T.new($.value + $b.value) } method div($b) { T.new($.value / $b.value) } }
The parameter in square brackets after the role name restricts both the type of the $.value attribute and the return type of the methods, which return a new object of the type T. Here, in the template of the role, T is just a name, which should later be specified when the role is used.
Using the role
So, let’s make it integer:
class N does R[Int] {}
Now the parts of the role that employ the T name replace it with Int, so the class is equivalent to the following definition:
class C { has Int $.value; method add($b) { Int.new($.value + $b.value) } method div($b) { Int.new($.value / $b.value) } }
The new class operates with integers, and the result of the division is an exact 3:
class N does R[Int] {} my N $i = N.new(value => 10); my N $j = N.new(value => 3); say $i.add($j); # 13 say $i.div($j); # 3
It is also possible to force floating-point values by instructing the role accordingly:
class F does R[Num] {}
my F $x = F.new(value => 10e0);
my F $y = F.new(value => 3e0);
say $x.add($y); # 13
say $x.div($y); # 3.33333333333333
Notice that both values, including 13, are of the Num type now, not Int or Rat as it was before:
say $x.add($y).WHAT; # (Num) say $x.div($y).WHAT; # (Num)
2 thoughts on “🦋17. Parameterised roles in Perl 6”