The example below is combines traits (S14) Lvalue subs (S06) to create a base class SimpleTiedHash that ties Attributes to the hash using the 'Entry' accessor.
PDFCatalog is an example instance class. This mostly works well. But SimpleTiedHash currently has a hacky way of overriding the built-in accessor. -- The :entry trait detects then overrides the Attribute `has_accessor property, setting it back to false. This `fools` Perl6 class system into not generating a standard accessor. -- The SimpleTiedHash.compose method only needs to be invoked once per class instance initialization, but is currently getting called too late and repeatedly via the new method. Is there a better way of adding custom accessors from traits, just once when classes are being initialized? Code sample below - David ================================== my role TiedAtt { has Bool $.is-entry = True; has Bool $.gen-accessor is rw; has method has_accessor { False } } multi trait_mod:<is>(Attribute $att is rw, :$entry!) is export(:DEFAULT) { # fool rakudo into not generating a standard accessor my $gen-accessor = $att.has_accessor; $att does TiedAtt; $att.gen-accessor = $gen-accessor; } class SimpleTiedHash is Hash { # base class sub att-accessor($obj, Str $key, Attribute $att) { Proxy.new( FETCH => method { $obj{$key}; }, STORE => method ($val is copy) { # simple typecheck die "$key: {$val.perl} has wrong type" unless $val ~~ $att.type; $obj{$key} = $val; }); } # create accessors method compose { for self.^attributes.grep({ .name ~~ /^'$!'<[A..Z]>/ && .can('is-entry'\ ) }) -> $att { my $key = $att.name.subst(/^'$!'/, ''); warn "setting up attribute $key"; if $att.gen-accessor && ! self.^declares_method($key) { $att.set_rw; self.^add_method( $key, method { att-accessor(self, $key, $att ) } ); } } } method new(|c) { my $obj = callsame; # happening too late. # should ideally be invoked once during class initialisation warn "composing..."; $obj.compose(); $obj; } } class PDFCatalog is SimpleTiedHash { # data instance class has Str $.Version is entry; has Hash $.Pages is entry; method ping{say 42} } my $cat = PDFCatalog.new; $cat.ping; $cat.Version = '1.3'; $cat.Pages = { :Count(0), :Kids[] }; say $cat.Version; say $cat<Version>; say $cat.Pages.perl;