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.