Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re^2: [Raku] Assigning defaults to attributes that are fixed length lists (and other confusions)

by tomgracey (Scribe)
on Apr 28, 2021 at 20:00 UTC ( [id://11131819]=note: print w/replies, xml ) Need Help??


in reply to Re: [Raku] Assigning defaults to attributes that are fixed length lists (and other confusions)
in thread [Raku] Assigning defaults to attributes that are fixed length lists (and other confusions)

Hi Dean - thanks for that, you really cleared a lot up there.

"The code has @.items[3] is rw; creates a positional attribute which contains a fixed-length array with 3 elements. Thus, the array is truthy and the unless is not triggered."

I guess this is just a matter of convention and I am happy for fixed (non-zero) length arrays to be regarded as "true". That would make me right about the error message being slightly misleading, I think?

Thanks for steering me in the direction of writing my own accessor. I am aware that this is an option - though I haven't experimented with it yet. Actually I was hoping it could be done with has, as I am expecting (too much of?) Raku code to be pretty and boilerplate free. The solution you suggested is a great help - but does seem like quite a lot of code lines. I guess this is just because it's a workaround until the ability to assigning defaults to fixed length arrays gets implemented. Then it will be reduced back to a single line (I suppose).

Thanks again for your help :)

  • Comment on Re^2: [Raku] Assigning defaults to attributes that are fixed length lists (and other confusions)
  • Select or Download Code

Replies are listed 'Best First'.
Re^3: [Raku] Assigning defaults to attributes that are fixed length lists (and other confusions)
by tomgracey (Scribe) on Apr 29, 2021 at 15:53 UTC

    Posting a quick update to this as my new and improved understanding of attributes led me to a simple alternative which gave me what I wanted. I realised my main problem was the unless having the wrong effect. The phenomenon of overwriting the fixed length array with a longer one is really just a curiosity, since I don't think there's any practical situation when you'd define an N sized array, and then specify an N + 1 element array as the default. I think the unless issue can be resolved easily in either of 2 ways, depending on what effect is desired:

    class Hamper { has $.name = 'Christmas Basket'; my Str @item_defaults[3] = 'Mince Pie', 'White Wine', 'Stinky Chee +se'; has Str @.items[3] is rw; method TWEAK(){ @!items[ $_ ] ||= @item_defaults[ $_ ] for @item_defaults.keys +; } } my $hamper = Hamper.new; say "Name: " ~ $hamper.name; say "Items: " ~ $hamper.items;

    This simply has code in TWEAK assign defaults on a per element basis. This way you get defaults at the element level, so e.g. if you do this:

    my $hamper = Hamper.new( items => ('Fish', 'Canoe') );

    $hamper.items will end up with the user specified values Fish and Canoe as the first 2 elements, and Stinky Cheese as the 3rd. This might be good for some purposes, but in my case I wanted the defaults to be completely overwritten if the user specified any values for @!items at all. ie in the above example, asking for $hamper.items should return "uninitialized value" for the third item. Only if I don't try to set any items at all should I get the defaults. This seems quite straightforward to implement with grep:

    method TWEAK(){ @!items = @item_defaults unless grep {$_}, @!items; }

    This now achieves everything I hoped:

    • I can insert up to 3 values
    • I get an error trying to insert more
    • I get an error trying to insert a non-Str value
    • I get a set of defaults if I don't specify any at all

    I think if you want exactly 3 (or however many) elements, then writing your own accessor is very likely your best option. (Or perhaps subclassing Array? Not really sure of the details there)

    Anyway, I just thought I'd share in case it helps anyone else out...

      Hi Tom... with you on the SO tone ... here's what I think is going on:

      1. An empty Array is falsey, yet an empty shaped Array is truthy (I would be interested to know why!) - thus, as you have already noted, the original error message is wrong in the suggestion for TWEAK. (You may wish to raise a bug issue at rakudostar on git.) More at https://stackoverflow.com/questions/67373726
      2. You are getting a compiler warning from trying to say ~$hamper.items where there is an empty value (Any) in a shaped Array... this does not happen with a non-shaped Array as the length auto-adjusts to the contents.
        Use of uninitialized value element[2] of type Any in string context. Methods .^name, .raku, .gist, or .say can be used to stringify it to s +omething meaningful. in block <unit> at test.raku line 14
        So, I propose you implement a Str method to bypass (Any) elements and format the object output for .put (or say ~$hamper).
      3. Array elements are each scalar containers and are each rw in any case - has @.items is rw just allows the top level array to be assigned to via a generated setter method. (NB. TWEAK can use the private @!items variable directly to assign the defaults).
      4. You can see this now gives low boilerplate accessors that do what you want (?) and protect from writing beyond 3 elements.
      class Hamper { has $.name = 'Christmas Basket'; has @.items[3]; method TWEAK { @!items = ['Mince Pie', 'White Wine', 'Stinky Cheese'] unless +@!items.any.so; } method Str { "Name: {$!name}\nItems: [{@!items.grep(*.so).join(', ')}]\n"; } } given Hamper.new { .items[2] = 'Hard Cheese'; .put; } #Name: Christmas Basket #Items: [Mince Pie, White Wine, Hard Cheese] given Hamper.new( items => ['Fish', 'Canoe'] ) { .put; .items[2] = 'Bicycle'; .put; #.items[3] = 'Horse'; #fails.. Index 3 for dimension 1 out of +range (must be 0..2) #.items.push: 'Blue Nun'; #fails.. Cannot push a fixed-dimension a +rray } #Name: Christmas Basket #Items: [Fish, Canoe] #Name: Christmas Basket #Items: [Fish, Canoe, Bicycle]
        An empty Array is falsey, yet an empty shaped Array is truthy

        I will hazard a guess that Raku handles arrays in scalar context similarly to Perl 5: an array evaluated in scalar context produces the number of elements in the array. An empty array has zero elements, but a shaped array has a fixed number of elements. In boolean context, zero is false and any non-zero number is true.

        Thus, an empty array (with zero elements) is a false value, but an array with a fixed non-zero number of elements is a true value.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11131819]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (6)
As of 2024-04-23 16:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found