J Hall
J Hall

Reputation: 531

Override Attribute of a Role

Is it possible to override an attribute of a role to provide a default?

role A {
     has $.a;
}
class B does A {
    has $.a = "default";
}
my $b = B.new;

This results in a compile error:

===SORRY!=== Error while compiling:
Attribute '$!a' already exists in the class 'B', but a role also wishes to compose it

Upvotes: 6

Views: 111

Answers (2)

user5854207
user5854207

Reputation:

Since methods in R may refer to $!a there would be ambiguity what attribute is referred to.

Use the submethod BUILD to initialise inherited/mixedin attributes.

role R { has $.a };
class C does R {
    submethod BUILD { $!a = "default" }
};
my $c = C.new;
dd $c;
# OUTPUT«C $c = C.new(a => "default")␤»

Depending on your usecase you may be better of to set the default via a role parameter.

role R[$d] { has $.a = $d };
class C does R["default"] { };
my $c = C.new;
dd $c;
# OUTPUT«C $c = C.new(a => "default")␤»

Upvotes: 5

Christoph
Christoph

Reputation: 169523

No, it's not possible to redeclare an attribute - but you can initialize it using the BUILD submethod of the class:

role A {
     has $.a;
}

class B does A {
    submethod BUILD(:$!a = 'default') {}
}

Note that if you just set the value within the body of BUILD instead of its signature via

class B does A {
    submethod BUILD { $!a = 'default' }
}

the user would not be able to overwrite the default by providing a named initializer via B.new(a => 42).

Another, more elegant approach that is especially useful if setting the default is something you expect to do a lot (ie the default value can be regarded as part of the role's interface) is making it a parameter:

role A[$d] {
    has $.a = $d;
}

class B does A['default'] {}

Upvotes: 2

Related Questions