-
Notifications
You must be signed in to change notification settings - Fork 293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revise and add to Attribute's documentation #3441
base: main
Are you sure you want to change the base?
Changes from 2 commits
e1ad867
ede882c
2ca3dce
c0d302d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,42 @@ In Raku lingo, an I<attribute> refers to a per-instance/object storage slot. | |
An C<Attribute> is used to talk about classes' and roles' attributes at the | ||
metalevel. | ||
|
||
Normal usage of attributes does not require the user to use this class | ||
explicitly. | ||
C<Attribute> is typically useful when working with the L<MOP|/language/mop>. | ||
For instance, the names of all the attributes of a type can be introspected | ||
using its L<name|#method_name> method: | ||
|
||
=begin code :solo | ||
class WithAttributes { | ||
has $.attribute; | ||
has $.attribute-two-electric-boogaloo; | ||
has $.yet-another-attribute; | ||
} | ||
|
||
.say for WithAttributes.^attributes(:local).map(*.name); | ||
# OUTPUT: | ||
# $!attribute | ||
# $!attribute-two-electric-boogaloo | ||
# $!yet-another-attribute | ||
=end code | ||
|
||
JJ marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Because of C<Attribute>, a type containing attributes, such as this: | ||
|
||
=for code :solo | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, not needed. |
||
class WithAttribute { | ||
has $.attribute; | ||
} | ||
|
||
Is not something only the compiler knows how to generate. This class in | ||
particular can be generated manually like so: | ||
|
||
=for code :solo | ||
BEGIN { | ||
constant WithAttribute = Metamodel::ClassHOW.new_type: :name<WithAttribute>; | ||
WithAttribute.^add_attribute: Attribute.new: | ||
:name<$!attribute>, :type(Any), :package(WithAttribute), | ||
:1has_accessor; | ||
WithAttribute.^compose; | ||
} | ||
|
||
=head1 Traits | ||
|
||
|
@@ -39,8 +73,6 @@ argument of C<is default> will set the default item value or hash value. | |
|
||
=head2 X<Trait is required|trait,is required (Attribute)> | ||
|
||
Defined as: | ||
|
||
multi sub trait_mod:<is> (Attribute $attr, :$required!) | ||
|
||
The trait C<is required> will mark the attribute as to be filled with a value | ||
|
@@ -104,8 +136,6 @@ STDERR: | |
|
||
=head2 X<trait is rw|trait,is rw (Attribute)> | ||
|
||
Defined as: | ||
|
||
multi sub trait_mod:<is> (Attribute:D $attr, :$rw!) | ||
|
||
Marks an attribute as read/write as opposed to the default C<readonly>. The | ||
|
@@ -124,47 +154,72 @@ default accessor for the attribute will return a writable value. | |
|
||
=head2 X<trait is built|trait,is built (Attribute)> | ||
|
||
Defined as: | ||
|
||
multi sub trait_mod:<is>(Attribute:D $a, :$built!) | ||
coke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
By default, this trait allows setting up a I«private attribute» during object | ||
construction via C«.new». The same trait can be used to prevent setting up a | ||
I«public attribute» via C«.new» by passing it the boolean value C«False». | ||
By default, this trait allows setting up a I«private attribute» during | ||
object construction via C«.new». The same trait can be used to prevent | ||
setting up a I«public attribute» via C«.new» by passing it the boolean | ||
value C«False». Setting up an attribute with its value is ordinarily | ||
handled by assigning the value, but if C<:bind> is passed, then it will | ||
be bound instead: | ||
|
||
class Foo { | ||
has $!bar is built; # same as `is built(True)` | ||
has $.baz is built(False); | ||
has $!qux is built(:bind); | ||
|
||
method bar { | ||
$!bar | ||
} | ||
method bar(::?CLASS:D:) { $!bar } | ||
method qux(::?CLASS:D:) { $!qux } | ||
} | ||
|
||
my $foo = Foo.new(bar => 1, baz => 2); | ||
say $foo.bar; # «1» | ||
say $foo.baz; # «Any» | ||
my Foo:D $foo .= new: :bar[], :baz[], :qux[]; | ||
say $foo.bar.raku; # OUTPUT: «$[]» | ||
say $foo.baz.raku; # OUTPUT: «Any» | ||
say $foo.qux.raku; # OUTPUT: «[]» | ||
|
||
=head1 Methods | ||
|
||
The usual way to obtain an object of type C<Attribute> is by introspection: | ||
|
||
class Useless { | ||
has @!things; | ||
} | ||
my $a = Useless.^attributes(:local)[0]; | ||
say $a.raku; # OUTPUT: «Attribute.new» | ||
say $a.name; # OUTPUT: «@!things» | ||
say $a.package; # OUTPUT: «(Useless)» | ||
say $a.has_accessor; # OUTPUT: «False» | ||
|
||
Modifying a private attribute from the outside is usually not possible, but | ||
since Attribute is at the level of the metaclass, all is fair game. | ||
=head2 method new | ||
|
||
=for code :skip-test<too long a method signature for one line> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In principle, there should be no problem with so many lines, as long as you use =for code or =begin code |
||
method new(Attribute:_: | ||
:$name!, :$type!, :$package!, | ||
:$inlined = 0, :$has_accessor = 0, :$is_built = $has_accessor, :$is_bound = 0, | ||
:$positional_delegate = 0, :$associative_delegate = 0, *%other) | ||
|
||
Creates a new attribute. The following named arguments are required: | ||
|
||
- C<$name> contains the attribute's name, which should always be a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe use =item here? |
||
string prefixed with the attribute's sigil and the I<!> twigil. | ||
- C<$type> contains type of the attribute. When creating untyped | ||
attributes, C<Any> should be passed. | ||
- C<$package> contains the type to which the attribute belongs to. | ||
|
||
Additional named arguments may optionally be included: | ||
|
||
- If C<$inlined> is set to C<1>, the attribute created will behave as if | ||
it were declared with | ||
L<HAS|/language/nativecall#Embedding_CStructs_and_CUnions> as its scope. | ||
- If C<$has_accessor> is set to C<1>, then an accessor method for the | ||
attribute will be generated for the attribute's package upon | ||
composition. | ||
- If C<$is_built> is set to C<1>, then this attribute will behave as if | ||
it had the L<C<is built>|#trait_is_built> trait applied to it. | ||
- If C<$is_bound> is set to C<1>, then this attribute will behave as if | ||
it had the L<C<is built>|#trait_is_built> trait applied to it with | ||
C<:bind> as its argument. | ||
|
||
The remaining named parameters are heavily dependent on Rakudo's | ||
internals, and are not something that should ordinarily be relied upon. | ||
|
||
=for comment | ||
That last sentence is a bit of a lie since there are a couple other | ||
named parameters that may be useful in some niche cases, but container | ||
descriptors and how autovivification works internally are undocumented | ||
as of writing. | ||
|
||
=head2 method name | ||
|
||
Defined as: | ||
|
||
method name(Attribute:D: --> Str:D) | ||
|
||
Returns the name of the attribute. Note that this is always the private name, | ||
|
@@ -176,11 +231,26 @@ so if an attribute is declared as C<has $.a>, the name returned is C<$!a>. | |
my $a = Foo.^attributes(:local)[0]; | ||
say $a.name; # OUTPUT: «@!bar» | ||
|
||
=head2 method package | ||
=head2 method type | ||
|
||
method type(Attribute:D: --> Mu) | ||
|
||
Returns the type constraint of the attribute. | ||
|
||
class TypeHouse { | ||
has Int @.array; | ||
has $!scalar; | ||
has @.mystery; | ||
} | ||
my @types = TypeHouse.^attributes(:local)[0..2]; | ||
for 0..2 { say @types[$_].type } | ||
# OUTPUT: «(Positional[Int]) | ||
# (Mu) | ||
# (Positional)» | ||
|
||
Defined as: | ||
=head2 method package | ||
|
||
method package() | ||
method package(Attribute:D:) | ||
|
||
Returns the package (class/grammar/role) to which this attribute belongs. | ||
|
||
|
@@ -192,8 +262,6 @@ Returns the package (class/grammar/role) to which this attribute belongs. | |
|
||
=head2 method has_accessor | ||
|
||
Defined as: | ||
|
||
method has_accessor(Attribute:D: --> Bool:D) | ||
coke marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Returns C<True> if the attribute has a public accessor method. | ||
|
@@ -209,8 +277,6 @@ Returns C<True> if the attribute has a public accessor method. | |
|
||
=head2 method rw | ||
|
||
Defined as: | ||
|
||
method rw(Attribute:D: --> Bool:D) | ||
|
||
Returns C<True> for attributes that have the "is rw" trait applied to them. | ||
|
@@ -226,8 +292,6 @@ Returns C<True> for attributes that have the "is rw" trait applied to them. | |
|
||
=head2 method readonly | ||
|
||
Defined as: | ||
|
||
method readonly(Attribute:D: --> Bool:D) | ||
|
||
Returns C<True> for readonly attributes, which is the default, or C<False> for | ||
|
@@ -244,8 +308,6 @@ attributes marked as C<is rw>. | |
|
||
=head2 method required | ||
|
||
Defined as: | ||
|
||
method required(Attribute:D: --> Any:D) | ||
|
||
Returns C<1> for attributes that have the "is required" trait applied, or C<Mu> | ||
|
@@ -261,28 +323,22 @@ is applied with a string, then that string will be returned instead of C<1>. | |
say $addr.required; # OUTPUT: «1» | ||
say $new-books.readonly; # OUTPUT: «"we always need more books"» | ||
|
||
=head2 method type | ||
=head2 method is_built | ||
|
||
Defined as: | ||
method is_built() | ||
|
||
method type(Attribute:D: --> Mu) | ||
Returns C<True> if the attribute had the L<C<is built>|#trait_is_built> | ||
trait applied to it, otherwise returns C<False>. | ||
|
||
Returns the type constraint of the attribute. | ||
=head2 method is_bound | ||
|
||
class TypeHouse { | ||
has Int @.array; | ||
has $!scalar; | ||
has @.mystery; | ||
} | ||
my @types = TypeHouse.^attributes(:local)[0..2]; | ||
for 0..2 { say @types[$_].type } | ||
# OUTPUT: «(Positional[Int]) | ||
# (Mu) | ||
# (Positional)» | ||
method is_bound() | ||
|
||
=head2 method get_value | ||
Returns C<True> if the attribute had the L<C<is built>|#trait_is_built> | ||
trait applied to it with C<:bind> as an argument, otherwise returns | ||
C<False>. | ||
|
||
Defined as: | ||
=head2 method get_value | ||
|
||
method get_value(Mu $obj) | ||
|
||
|
@@ -299,8 +355,6 @@ used with care. Here be dragons. | |
|
||
=head2 method set_value | ||
|
||
Defined as: | ||
|
||
method set_value(Mu $obj, Mu \new_val) | ||
|
||
Binds the value C<new_val> to this attribute of object C<$obj>. | ||
|
@@ -320,8 +374,6 @@ used with care. Here be dragons. | |
|
||
=head2 method gist | ||
|
||
Defined as | ||
|
||
multi method gist(Attribute:D:) | ||
|
||
Returns the name of the type followed by the name of the attribute. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why :solo? This would compile correctly, right? And it does not need its own file.