Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

[raku] what accessors are actually autogenerated for @.array attributes?

by tomgracey (Scribe)
on May 06, 2021 at 14:22 UTC ( [id://11132158]=perlquestion: print w/replies, xml ) Need Help??

tomgracey has asked for the wisdom of the Perl Monks concerning the following question:

Hello PerlRaku Monks

I have another (absurdly basic) Raku question. Consider the following:

class Fish { has @.scales is rw; } my $fish = Fish.new; $fish.scales('green','blue','yellow');

This doesn't work. I get:

Too many positionals passed; expected 1 argument but got 4

The "1 argument" it appears to be expecting seems to be self. ie it seems specifying @.scales creates only the get accessor. Is this correct? Do we have to create our own set accessors for array-type attributes?

I may be looking in the wrong place, but I could not find any documentation on exactly what accessors are autogenerated - the docs seem to just say "specify the period twigil to have accessors autogenerated".

Part of my problem may be that I am not familiar enough with the various inspection methods. Possibly there is a way to see exactly what methods are being created. For example the ^methods meta-method does return a list of methods, but without any accompanying information - e.g is it a multimethod etc. So scales would come up in the list if $fish.^methods is called, but it would seem only once regardless if there is a setter. Is anyone aware of a way of getting a more detailed list?

Sorry again for the basic question, but sometimes it seems better to get some direct feedback rather than trawling endlessly in the hope of finding examples. (I looked through vast swathes of code for an example of setting an array after the object has been created and didn't happen on any at all. Plenty of setting it via new of course. Not sure if I was just unlucky.)

Anyway thanks in advance for your help

Replies are listed 'Best First'.
Re: [raku] what accessors are actually autogenerated for @.array attributes?
by choroba (Cardinal) on May 06, 2021 at 15:00 UTC
    I don't know much about Raku, but the following worked for me:
    $fish.scales = ('green','blue','yellow');

    Interestingly, it also works if I declare the attribute as readonly.

    See also StackOverflow.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: [raku] what accessors are actually autogenerated for @.array attributes?
by markldevine (Initiate) on May 10, 2021 at 20:21 UTC

      Please learn how to format your posts properly.

Re: [raku] what accessors are actually autogenerated for @.array attributes?
by p6steve (Sexton) on May 11, 2021 at 21:33 UTC
    raku is a well behaved language in that concepts are kept "orthogonal" where possible. So from the docs there are three concepts in play:
    1. the sigil - @ (or $, %, &)- provides guidance on the content of your attribute container - in the case of @ it does role Positional
    2. the twigil - . or ! - provides guidance on the encapsulation, a "public attribute" with '.' has a getter accessor method automatically generated - with 'is rw' both getter and setter methods are provided
    3. the actual contents - in this case an Array - and each item in an Array is a scalar container that may be assigned a value
    You are experiencing the implications of this design in a number of ways:
    • the twigil and is rw trait apply at the attribute container level - so you can assign an entirely different Array to your attribute if it is marked 'is rw', regardless of this, when you have the Array (even if the container is read only) individual items are still read/write and may be assigned values, sliced, pushed/popped (unless a where constraint or type subset is provided) - you can assign a List if you want immutability
    • the generated accessor method therefore applies at the top level - and, as with $ scalars, the setter method provides an lvalue that can be assigned to with the '=' operator so $fish.scales = ('green','blue','yellow'); is fine
    • raku public attributes are (deliberately) simple - so that is all you get from the '.' twigil ... I think that the idea is to make raku OO be very lightweight in the role of random data structure, but to have encapsulation also be very light and to kick in for all use cases that have even just the slightest type management demands - the colon formulation of the method call is awesome for this $fish.scales: ('green','blue','yellow');
    1 class Fish { 2 has @!scales; 3 4 multi method scales { @!scales } 5 multi method scales( @new ) { @!scales = @new } 6 method raku { "[ scales => {@!scales} ]" } 7 } 8 9 my $fish = Fish.new; 10 $fish.scales: ('green','blue','yellow'); 11 12 dd $fish;
Re: [raku] what accessors are actually autogenerated for @.array attributes?
by b2gills (Novice) on May 13, 2021 at 16:01 UTC

    When you declare a public attribute (with the . twigil) you are telling it (among other things) to create a method with the same name.

    class Fish { has @.scales; }
    class Fish { has @!scales; method scales () { return @!scales } # other stuff it does (not exhaustive) submethod BUILD ( :@!scales ){} method gist () { … } method Capture () { \( :@!scales ) } }

    When you declare it as is rw, you are telling it that the generated method should also be marked as is rw.

    class Fish { has @.scales is rw; }
    class Fish { has @!scales; # v---v method scales () is rw { return-rw @!scales } # other stuff it does (not exhaustive) submethod BUILD ( :@!scales ){} method gist () { … } method Capture () { \( :@!scales ) } }

    So then when you assign to the method, you replace the contents of the array.

    my $fish = Fish.new; $fish.scales = ('green','blue','yellow')

    If you instead wanted to handle it with the parameter list, you would replace the generated one with a method you created yourself.

    (You still want to make it public with . so that the other stuff happens)

    class Fish { has @.scales; multi method scales () { @!scales } multi method scales ( +@new ) { @!scales = @new } } my $fish = Fish.new; $fish.scales('green','blue','yellow'); say $fish.scales(); # does not clear it $fish.scales(()); # clears it

    You can do it with an only method if you prefer.

    class Fish { has @.scales; method scales ( |c (+@new) ) { @!scales = @new if c; return @!scales; } } my $fish = Fish.new; $fish.scales('green','blue','yellow'); say $fish.scales(); # does not clear it $fish.scales(()); # clears it

    The |c is necessary so that an empty list given as the first and only argument will clear @!scales, but a truly empty argument list doesn't.


    There is one thing that you should know, Array and Hash attributes currently don't need is rw to be mutable. They are mutable already.

    class Fish { has @.scales; } my $fish = Fish.new; $fish.scales = ('green','blue','yellow'); say $fish.scales(); $fish.scales = (); # clears it

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11132158]
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (4)
As of 2024-04-25 16:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found