Following the documentation and this SO answer I wrote the following class with clone method, but the tests in the code do not give the expected result.
use v6.d;
use Test;
class Numeration {
has Int @!counters is default(1);
submethod TWEAK( ) { @!counters = Nil }
multi method Str () {
@!counters>>.Str.join('.') ~ '.'
}
multi method Str ( $n ) {
@!counters[^$n]>>.Str.join('.') ~ '.'
}
multi method inc (Int() $level) {
@!counters[$level - 1]++;
@!counters.splice($level);
self
}
multi method inc () {
self.inc(1)
}
method reset () {
@!counters = Nil;
self
}
method set (Int() $level, $value ) {
@!counters[$level - 1] = $value;
self
}
multi method clone {
nextwith :counters(@!counters.clone), |%_
}
}
my Numeration $n .= new;
$n.inc;
is $n.set(5,3).Str, '2.1.1.1.3.', 'set beyond last extent puts 1 in intermediate position';
my $m = $n.clone;
is $m.Str, '2.1.1.1.3.', 'same as n';
is $m.inc(3).Str(5), '2.1.2.1.1.', 'increments properly';
is $n.Str(5), '2.1.1.1.3.', 'incrementing m leaves n unchanged';
The last test fails.
I am not sure why.
Update: if the line
has Int @!counters is default(1);
is changed to
has Int @.counters is default(1);
then the final test now returns ok.
Why does adding the automatic accessor make the clone method work?
After discussing this with @lizmat, it seems that using nextwith inside the clone method leads to a call to new, but new only picks up public attributes (those declared with has @.counters) but not private ones (eg has @!counters).
note the difference between '.' and '!'
So a different approach is needed with clone, such
method !set-counters(@counters) {
@!counters := @counters;
self
}
multi method clone(Numeration:D:) {
callsame!set-counters(@!counters.clone)
}
This achieves the desired result.