Reputation: 6012
If I have a public method, I can call it inside its class using both $.name
and self.name
:
class TEST {
has Int $.a;
method b($x) {
return $!a * $x;
}
method c($y) {
return self.b($y) * 3; # or $.b($y)
}
}
my $m = TEST.new(a => 10);
say $m.c(2); # 60
But if I make b
a private method, I only can call it with self!b
, not $!b
, otherwise I get the following error message:
Attribute $!b not declared in class TEST
What's behind this rule? What are the rules of calling a method inside its own class?
Upvotes: 12
Views: 331
Reputation: 3086
NOTE: This is mostly a rehash of lizmat's answer but I figured the way I've come to reason about this issue might add some value.
From experienced Rakoons, I'd like to know if this way of wording the discourse about private vs public attributes in Raku makes sense, whether it's correct or simply misrepresents the issue.
$!
.$.method
but not $!method
.I'll start by saying that there are only private attributes in Raku, which you declare with $!
. This statement obviously depends on how you define private vs public attributes, and I even make the case by the end of the section on why the Raku community makes the distinction between them. In the context of this answer, a true public attribute is one that's truly accessible to the outside world upon initialization. For example JavaScript has true public attributes:
class Foo {
constructor() {
this.value = 42;
}
}
const bar = new Foo();
console.log(bar.value); #=> 42
bar.value = 56;
console.log(bar["value"]); #=> 56
The only thing that prevents the programmer from messing an object's state directly is through convention. Either the consumer of the class knows beforehand that a class's attributes shouldn't be accessed directly or there's some way of signaling which attributes shouldn't be accessed at all. For example, in both JavaScript and Python, the author of a class signals to other programmers an attribute is "private" by prefixing it with an underscore. Thus, in this sense, those languages only have public attributes which are protected from the outside through convention; Raku approaches it from the opposite side: it has only private attributes which are made accessible to the outside world through language constructs.
Now assuming you agree with the statement "there are only private attributes in Raku", let's continue. If there are only private attributes, we can simply drop the "private" qualifier since it's redundant. Unlike in other programming languages, you cannot access an attribute outside its class unless you go out of your way to facilitate it using methods either explicitly or implicitly. By creating methods explicitly, you can use access it, mutate it, etc., and doing these things is so common that Raku provides sensible defaults to access, mutate, and restrict attributes using constructs such as traits.
The following table is an attempt at summarizing these defaults (I've prefixed each row in the first column with "(private)" as a reminder you're always dealing with private attributes in Raku classes):
You want a | You declare it with (assume has ) |
Comment |
---|---|---|
(private) attribute | $!attr |
Attribute isn't accessible to the outside world |
(private) attribute that can be set in the default constructor | $!attr is built |
Attribute can be set in the default constructor, otherwise it's not accessible to the outside world |
(private) attribute with an accessor method | $.attr |
Attribute is completely accessible to the outside world, including setting it in the default constructor |
(private) attribute with an accessor method but cannot be set in the default constructor | $.attr is built(False) |
Attribute is accessible as read-only, and thus cannot be set in the default constructor |
(private) attribute with a mutator method | $.attr is rw |
Attribute is completely accessible to the outside world, including mutating it after initialization as obj.attr = value |
(private) attribute with an accessor method and not optional at object initialization | $.attr is required |
Attribute is completely accessible to the outside world, and must be set at object initialization |
(private) attribute that can be set in the default constructor and not optional at object initialization | $!attr is built is required |
Attribute must be set in default constructor, otherwise it's not accessible to the outside world |
Now "(private) attribute with an accessor/mutator method" is quite the mouthful, which is why the Raku community calls attributes declared with $.
(or any other sigil) public attributes, with the proviso the reader understands they're indeed private attributes with a few behaviors tacked onto them as dictated by the twigil (.
) and the applied trait. This is the case I alluded to at beginning: despite not being public per se by the above definition, it makes sense to call them public since they exhibit many of the things a true public attribute is, especially in contrast to private attributes. It also makes talking about them more succint and easier, e.g., "(private) attribute with an accessor method but cannot be set in the default constructor" simply becomes "public attribute that cannot be set in the default constructor".
I think that knowing now $.attr
is an attribute with an accessor method makes it easier to understand why you can access the attribute either with $.attr
or self.attr
: you're simply accessing the attribute through its accessor method. However, as explained below, a method such as $.method
isn't necessarily tied to an attribute per se: all public methods can be invoked using that syntax.
self
and sigilsIn general, contextualized method invocation, e.g., $.method
, simply means you're applying the sigil's context to the method's return value. Thus, for example, $.method
is a shorthand for $(self.method)
or self.method.item
. However since $.
is also used to declare public attributes, using it with a method might mislead the reader into thinking there's an attribute with that same name when there might not be one. Similarly, it might mislead the beginner Rakoon into thinking private method invocation using sigils, e.g., $!method
, is possible when in fact doing so simply throws a compilation error. Because contextualized method invocation can be misleading, I suggest a bit of prescriptivism ❷:
Avoid using $.
, @.
, or %.
inside a class unless to declare a public attribute:
$!
, @!
, or %!
, e.g., $!attr
.self
, i.e., self.attr
.self.attr.item
or $(self.attr)
.❶ I just came up with this but it's more compact than "method invocation using sigils" or listing out the sigils.
❷ This doesn't make much sense in the Land of TIMTOWTDI but try we must.
Upvotes: 1
Reputation: 26969
An attribute can always be referred to as $!foo
in a class. If you do that, than the code will be generated to directly access the attribute itself, and any classes subclassing your class will not be able to change this behaviour.
If you use has $.foo
in the declaration of a class, it means that a public accessor will be created for you (and if you add is rw
it can also function as a mutator).
When you use $.foo
in your code otherwise, it is exactly the same as $( self.foo )
. This means that it will call the method foo
on self
, and itemize the return value (make it a single "thing" if it wasn't yet). This will go wrong if you defined your attribute with $!foo
and you did not supply a method foo
yourself.
This goes even further: $.bar
really means self.bar
: you only need to have a method existing by the name bar
, which may not be related to any attribute at all.
If you define a private method !baz
, the !
just indicates the privacy of the method, which means you need to call it indeed as self!baz
. There is no short syntax for it.
Personally I dislike the fact that you can say $.zippo
even if zippo
is not an attribute. But I'm afraid that ship has sailed. But this behaviour is now causing you confusion :-(
So what's behind the rule for not having a short syntax for calling a private method? Not sure, I guess really that $!foo
was already taken to mean direct access to the attribute, and provide you with a compile time error if the attribute doesn't exist.
Hope this answers your question!
Upvotes: 11