diff --git a/doc/Type/Attribute.pod6 b/doc/Type/Attribute.pod6 index 57ee84171..0c2be4b09 100644 --- a/doc/Type/Attribute.pod6 +++ b/doc/Type/Attribute.pod6 @@ -10,8 +10,44 @@ In Raku lingo, an I refers to a per-instance/object storage slot. An C 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 is typically useful when working with the L. +For instance, the attributes of any type that supports them can be +introspected using the C metamethod, which returns a list of +C instances. Using these, we can inspect various properties of +a type's attributes, such as their names: + +=begin code +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 + +Because of C, a type containing attributes, such as this: + +=for code +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.^add_attribute: Attribute.new: + :name<$!attribute>, :type(Any), :package(WithAttribute), + :1has_accessor; + WithAttribute.^compose; +} =head1 Traits @@ -39,8 +75,6 @@ argument of C will set the default item value or hash value. =head2 X -Defined as: - multi sub trait_mod: (Attribute $attr, :$required!) The trait C will mark the attribute as to be filled with a value @@ -104,8 +138,6 @@ STDERR: =head2 X -Defined as: - multi sub trait_mod: (Attribute:D $attr, :$rw!) Marks an attribute as read/write as opposed to the default C. The @@ -124,47 +156,72 @@ default accessor for the attribute will return a writable value. =head2 X -Defined as: - multi sub trait_mod:(Attribute:D $a, :$built!) -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 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 +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 +string prefixed with the attribute's sigil and the I twigil. +- C<$type> contains type of the attribute. When creating untyped +attributes, C 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 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|#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|#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 +233,26 @@ so if an attribute is declared as C, the name returned is C<$!a>. my $a = Foo.^attributes(:local)[0]; say $a.name; # OUTPUT: «@!bar␤» -=head2 method package +=head2 method type -Defined as: + method type(Attribute:D: --> Mu) - method package() +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)␤» + +=head2 method package + + method package(Attribute:D:) Returns the package (class/grammar/role) to which this attribute belongs. @@ -192,8 +264,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) Returns C if the attribute has a public accessor method. @@ -209,8 +279,6 @@ Returns C if the attribute has a public accessor method. =head2 method rw -Defined as: - method rw(Attribute:D: --> Bool:D) Returns C for attributes that have the "is rw" trait applied to them. @@ -226,8 +294,6 @@ Returns C for attributes that have the "is rw" trait applied to them. =head2 method readonly -Defined as: - method readonly(Attribute:D: --> Bool:D) Returns C for readonly attributes, which is the default, or C for @@ -244,8 +310,6 @@ attributes marked as C. =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 @@ -261,28 +325,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 if the attribute had the L|#trait_is_built> +trait applied to it, otherwise returns C. -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 if the attribute had the L|#trait_is_built> +trait applied to it with C<:bind> as an argument, otherwise returns +C. -Defined as: +=head2 method get_value method get_value(Mu $obj) @@ -299,8 +357,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 to this attribute of object C<$obj>. @@ -320,8 +376,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. diff --git a/xt/code.pws b/xt/code.pws index 48541df48..4f27959dd 100644 --- a/xt/code.pws +++ b/xt/code.pws @@ -118,6 +118,7 @@ bloatable bloaty bn bol +boogaloo bq brakcet bt @@ -473,6 +474,7 @@ qrstuvwx qs quant quietlevel +qux quux qxelwq ranklist @@ -598,6 +600,8 @@ welp whatevera whateverable wincompose +withattribute +withattributes withoutlinenumber withtype wix