In Object orientation§
See primary documentation in context for Mixins of roles.
Roles can be mixed into objects. A role's given attributes and methods will be added to the methods and attributes the object already has. Multiple mixins and anonymous roles are supported.
role R { method Str() {'hidden!'} }; my $i = 2 but R; sub f(\bound){ put bound }; f($i); # OUTPUT: «hidden!» my @positional := <a b> but R; say @positional.^name; # OUTPUT: «List+{R}»
Note that the object got the role mixed in, not the object's class or the container. Thus, @-sigiled containers will require binding to make the role stick as is shown in the example with @positional. Some operators will return a new value, which effectively strips the mixin from the result. That is why it might be more clear to mix in the role in the declaration of the variable using does:
role R {}; my @positional does R = <a b>; say @positional.^name; # OUTPUT: «Array+{R}»
The operator infix:<but> is narrower than the list constructor. When providing a list of roles to mix in, always use parentheses.
role R1 { method m {} } role R2 { method n {} } my $a = 1 but R1,R2; # R2 is in sink context, issues a WARNING say $a.^name; # OUTPUT: «Int+{R1}» my $all-roles = 1 but (R1,R2); say $all-roles.^name; # OUTPUT: «Int+{R1,R2}»
If the role supplies exactly one attribute, an initializer can be passed in parentheses:
role Named { has $.name; } my $hero = 1.Rat but Named('Remy'); say $hero.name; # OUTPUT: «Remy»
Mixins can be used at any point in your object's life.
# A counter for Table of Contents role TOC-Counter { has Int @!counters is default(0); method Str() { @!counters.join: '.' } method inc($level) { @!counters[$level - 1]++; @!counters.splice($level); self } } my Num $toc-counter = NaN; # don't do math with Not A Number say $toc-counter; # OUTPUT: «NaN» $toc-counter does TOC-Counter; # now we mix the role in $toc-counter.inc(1).inc(2).inc(2).inc(1).inc(2).inc(2).inc(3).inc(3); put $toc-counter / 1; # OUTPUT: «NaN» (because that's numerical context) put $toc-counter; # OUTPUT: «2.2.2» (put will call TOC-Counter::Str)
Roles can be anonymous.
my %seen of Int is default(0 but role :: { method Str() {'NULL'} }); say %seen<not-there>; # OUTPUT: «NULL» say %seen<not-there>.defined; # OUTPUT: «True» (0 may be False but is well defined) say Int.new(%seen<not-there>); # OUTPUT: «0»