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

As Perl novice I am trying to learn how I can use structs in Perl. In my last question I got some good hints and now I am trying to do this using MooX instead of Class::Struct.

So far it looks quite straightforward to me, not as awkward like most other approaches. However, I surprisingly ran into a problem. Apparently one needs to explicitly declare structs as read/write. Structs in Perl seem by default like constants. Now the big question is how to achieve this.

Here the script that I try to run with perl -w. The script fails with the message: "tags is a read-only accessor at (eval 129) line 17." I have marked line 17 and as you see, it is empty...

use strict; use warnings; use MooX::Struct Document => [ qw($fileID $filename @tags) ]; Document => [ tags => [ is => 'rw', required => 1 ] ]; # the line above produces... # Useless use of a constant ("Document") in void context at testmoo.pl + line 5. # Useless use of anonymous list ([]) in void context at testmoo.pl lin +e 5. # ...instead of making the struct members writeable my @doc_list; # simulate processing two files "by hand" for testing # first document my $filenumber = 1; my $filename = "PetsFile"; my @filetags; # create, allocate and store struct in doc_list my $doc = Document[ $filenumber, $filename, @filetags]; push @doc_list, $doc; # <-- this is line 17, and the script fails with the message: # tags is a read-only accessor at (eval 129) line 17. # second document ++$filenumber; $filename = "BirdsFile"; # create, allocate and store struct in doc_list my $doc = Document[ $filenumber, $filename, @filetags]; push @doc_list, $doc; # simulated processing in a later pass # - this involves modifying struct members after creation my $tag = "Cats"; push @filetags, $tag; $tag = "Dogs"; push @filetags, $tag; $doc_list[1]->tags ( @filetags); my @filetags; # new my frees variable and resets to unde +f again $tag = "Doves"; push @filetags, $tag; $tag = "Eagles"; push @filetags, $tag; $tag = "Shantaks"; push @filetags, $tag; $doc_list[2]->tags ( @filetags); # finally check whether this worked as intended for (1..2) { # my $$doc_pointer = \@doc_list[ @_ ]; # mmh a pointer/hard ref would be nice... # could then avoid to use the index as in the code below... # need to learn how to do this printf "File ID: %s\n", $doc_list[ @_ ]->fileID; printf "Filename: %s\n", $doc_list[ @_ ]->filename; printf "Tags: %s\n", join(', ', @{ $doc_list[ @_ ]->tags }); }

Does anybody have an idea what I could have done wrong?

Replies are listed 'Best First'.
Re: How can I make struct members writeable using MooX::Struct?
by Loops (Curate) on Oct 28, 2014 at 22:27 UTC

    Well the first thing is that along with the use MooX... line you're trying to set two different values for "Document", and have ended up with an unbalanced number of brackets. Perl is giving you some other warnings too, you should work through each of them and post code here that doesn't have any warnings left in it, unless you're really stuck on one of them specifically.

    Okay, so here is your program pared down to its minimum, as a first step:

    use strict; use warnings; # Create a Document struct with all elements writeable: use MooX::Struct -rw, Document => [ qw( $fileID $filename @tags ) ]; # Create two documents and put them in an array; my $doc = Document[ 1, "PetsFile"]; my $doc2 = Document[ 2, "BirdsFile", ['Doves', 'Eagles']]; my @doc_list = ( $doc, $doc2 ); # Change the tags on both documents to prove they're writeable $doc_list[0]->tags( [qw(Cats Dogs)] ); push @{$doc2->tags}, 'Shantaks'; # Display the result. Notice that array indexes start at 0! for (0..1) { printf "File ID: %s\n", $doc_list[$_]->fileID; printf "Filename: %s\n", $doc_list[$_]->filename; printf "Tags: %s\n", join(', ', @{$doc_list[$_]->tags}); print "\n"; } # But here is a nicer way, without indexes: for my $d (@doc_list) { printf "File ID: %s\n", $d->fileID; printf "Filename: %s\n", $d->filename; printf "Tags: %s\n", join(', ', @{$d->tags}); print "\n"; }

      Thank you Loops! I read your post and then understood that in the second assignment I didn't just assign a rw property but that it was a complete new assignment. I finally found a way to merge both assignments, but I don't yet know whether they are correct. Then I returned to the monks page and read your update. Didn't know that the rw attribute can be set so easily :)

      Now I am reading your sample and understanding much more... You are showing me the "Perl way" :) Thank you so much!

Re: How can I make struct members writeable using MooX::Struct?
by Laurent_R (Canon) on Oct 29, 2014 at 07:37 UTC
    Just to correct a minor false assumption. Structs (whatever data structure you call this way, "struct" is not a Perl concept) are not constant by default in Perl. But objects constucted with MooX may be defined with ro or rw property and, although I am not using MooX, I guess that they are probably defaulted to ro.
Re: How can I make struct members writeable using MooX::Struct?
by Arunbear (Prior) on Nov 06, 2014 at 17:52 UTC
    Perl does not have structs (i.e. data that has a fixed set of named slots). The nearest equivalent would be a locked hash:
    use Hash::Util qw( lock_keys ); my %doc = ( filename => '', fileID => undef, tags => [] ); lock_keys(%doc);
    If you look behind the mask of a MooX::Struct, you'll find that it is just a hash and doesn't have the fixed keys property you may expect from real structs e.g.
    %> reply 0> use MooX::Struct -rw, Document => [ qw( $fileID $filename @tags ) +]; 1> my $doc = Document[ 1, "PetsFile"]; $res[0] = bless( { 'fileID' => 1, 'filename' => 'PetsFile' }, 'MooX::Struct::__ANON__::0001' ) 2> $doc->{foo} = 'bar'; $res[1] = 'bar' 3> delete $doc->{fileID}; $res[2] = 1 4> $doc $res[3] = bless( { 'filename' => 'PetsFile', 'foo' => 'bar' }, 'MooX::Struct::__ANON__::0001' ) 5>
    I used reply but you could also use Data::Dumper to do the inspection.