Using automatically generated methods for public attributes vs creating methods for accessing private attributes
Personally I'd go nice and simple :
class Foo {
has Int $.a;
has Int $!b = $!a ** 2;
has Int $!c = $!a ** 3;
method b { $!b }
method c { $!c }
}
say Foo.new(:2a, :1b); #=> Foo.new(a => 2)
say Foo.new(:2a, :1b).b; #=> 4
Just use the default constructor and default values for the attributes and add a couple of basic read methods. The default constructor only updates public attributes so if you try and override b
or c
this is ignored.
If you wanted you could add a BUILD
submethod to error if someone tries and sets them.
A good point raised in the comments for this case (and possibly the final use case) doing it like this :
class Foo {
has Int $.a;
method b() is pure { $!a ** 2 }
method c() is pure { $!a ** 3 }
}
say Foo.new(:2a, :1b); #=> Foo.new(a => 2)
say Foo.new(:2a, :1b).b; #=> 4
Depending on the complexity of the calculation you may want to use the is cached
trait too.
I read an article a couple of weeks ago that talked about this ambiguity between the constructor arguments of a class and its public interface but I cannot find it anymore.
But I am thinking you could leverage FALLBACK
.
class Bar {
has Int $.a;
has Int $!b;
has Int $!c;
submethod TWEAK {
$!b = $!a ** 2;
$!c = $!a ** 3;
}
method FALLBACK( $name ) {
self.^attributes.first( *.name eq "\$!$name" ).?get_value(self);
}
}
say Bar.new(:2a);
say Bar.new(:2a).c;
That's kinda hacky though, and costly since the attribute lookups have to go though FALLBACK
and introspection. What would be nice to have is a trait that would create an accessor but have no effect on the constructor. Something like
class Bar {
has Int $.a;
has Int $.b is shady; # or whatever name
has Int $.c is shady;
}
But I am not aware such a thing exists.