rakumetamodelrakudo

"compose_attributes" is apparently called before attributes are composed


This is another attempt to do this thing, essentially create a frozen class:

my class MetamodelX::Frozen is Metamodel::ClassHOW {

    method compose_attributes(Mu \type) {
        for self.attributes(type, :local ) -> $a {
            my $attr = Attribute.new( :0rw, :0has_accessor, :name( $a.name ),
             :type($a.type), :package($a.package));
            self.add_attribute(type, $attr );
        }
        callsame();
    }
}

my package EXPORTHOW {
    package DECLARE {
        constant frozen = MetamodelX::Frozen;
    }
}

Similarly to @jnthwrthngtn answer here, where methods were created in a specific shape to prevent the Class HOW creating them in the usual way, what I do here is to re-create the attributes in a read-only fashion and without accessor, with the rest of the mandatory attributes the same. This fail, however, when you try to instantiate it:

Package 'Foo' already has an attribute named '$!bar'

At any rare, this will only eliminate the accessor, because the attribute will already be read only, and will not manage to make a real frozen class (besides, it can still be augmented, so not frozen by a long shot). Any other way you can think of for trying to achieve the same?


Solution

  • Attribute composition is not the time when attributes are added to the class; rather, it is the time at which:

    I'm not clear on what you are intend to achieve, in so far as attributes are externally readonly by default anyway, and so it'd make more sense perhaps to produce an error if somebody defines an rw one on something declared frozen. If you do want to modify attributes, you'd probably have more luck overriding add_attribute and tweaking the way the attribute works at that point (for example, something like method add_attribute($obj, $attr) { callwith($obj, $attr.clone(:!rw, :!has_accessor)) } may achieve what you want).