Skip to content
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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 115 additions & 63 deletions doc/Type/Attribute.pod6
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

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.

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
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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>
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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,
Expand All @@ -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.

Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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>
Expand All @@ -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)

Expand All @@ -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>.
Expand All @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions xt/code.pws
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ bloatable
bloaty
bn
bol
boogaloo
bq
brakcet
bt
Expand Down Expand Up @@ -473,6 +474,7 @@ qrstuvwx
qs
quant
quietlevel
qux
quux
qxelwq
ranklist
Expand Down Expand Up @@ -598,6 +600,8 @@ welp
whatevera
whateverable
wincompose
withattribute
withattributes
withoutlinenumber
withtype
wix
Expand Down