Curt Tilmes
Curt Tilmes

Reputation: 3045

Can roles access included role attributes?

I have a class that does a role that does another role. A method from the class can access an attribute in the top level role:

role A {
    has $.x
}

role B does A {
}

class C does B {
    method this() { say $!x }
}

C.new(:x(1)).this;

That works fine and says 1 like I think it should.

But I have a number of classes that all do role B, that I want to share the method this(), so I move it up into role B:

role A {
    has $.x
}

role B does A {
    method this() { $!x }
}

class C does B {}

C.new(:x(1)).this;

That won't even compile: SORRY Attribute $!x not declared in role B.

Can a role not see attributes in a role it includes?

Upvotes: 7

Views: 164

Answers (1)

Jonathan Worthington
Jonathan Worthington

Reputation: 29454

Doing this requires the introduction of a private method in the first role and access through that:

role A {
    has $.x;
    method !x() { $!x }
}

role B does A {
    method this() { self!x }
}

class C does B {}

say C.new(:x(1)).this;  # 1

The reason the original code doesn't work - and isn't trivial to make it work - is that roles are generic and potentially overloaded with different sets of type parameters, so the mention of a role is itself generic. Therefore, in general, the does A does not identify a specific target role; there could be multiple of them, and they could have different attributes. It's not until the point that we compose the role into a class that we finally identify the concrete set of roles being composed.

However, attribute accesses are a kind of variable access, and so checked at the point we are compiling the role. In the final class, this isn't so much of a problem, since we are at that point choosing all the concrete roles we'll use; this is why it can easily work there. For the role case, we couldn't really check them until the point of composition into a final class, which could be in a separate compilation unit altogether. This is likely a solvable problem, and a future Perl 6 language version could specify a way for this to work. For now, it conservatively does not.

Upvotes: 7

Related Questions