Beefy Boxes and Bandwidth Generously Provided by pair Networks Joe
Perl-Sensitive Sunglasses
 
PerlMonks  

Re^4: Conditional array initialisation? (Question expanded)

by BrowserUk (Pope)
on Jul 12, 2013 at 21:24 UTC ( #1044082=note: print w/ replies, xml ) Need Help??


in reply to Re^3: Conditional array initialisation?
in thread Conditional array initialisation?

Nevertheless undef @a is allowed, adding even more confusion:

I don't see any confusion nor potential for it.

If I have a scalar and at some point in my code I wish to check if it currently has a value, and if it does not, set it, I might write:

my $x; ... $x = getValue() unless defined $x;

More recently, I'd write that as: $x //= getValue();. Clear concise semantics.

If have an array, and at some point in my code I want to check if it contains anything, and populate it if it does not, I might write:

my @a; ... @a = getValues() unless @a;

Thus using the value of the array in a scalar context to decide if the array is empty or not.

It seems a natural, semantically clear, easily implemented extension of the scalar case above to write that as:

@a //= getValues();

Test the scalar context value of the array, and if it is false, assign the list on the right hand side to the array on the left.

Indeed, it seemed so logically analogous, and semantically clear, that I wrote it and expected it to work, and I was taken by considerable surprise that it didn't.

Hence my OP.


With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.


Comment on Re^4: Conditional array initialisation? (Question expanded)
Select or Download Code
Re^5: Conditional array initialisation? (scalar, !defined)
by tye (Cardinal) on Jul 13, 2013 at 02:16 UTC
    If have an array, and at some point in my code I want to check if it contains anything,

    I'm not sure why you are throwing in "defined" in this scenario. Your scenario makes perfect sense for a Boolean test but, indeed, there was often confusion with "defined" related to your stated purpose:

    $ perl -del > x defined @a '' > @a = (1..3) > x defined @a 1 > @a = () > x defined @a 1

    That is, people were often asking "defined @a" when they meant "is @a empty". The mistake was so common (you appear to have just made that mistake yourself) that "defined @a" was made to complain.

    But that seems mostly a distraction from your original question, which should be restricted to ||= (since //= doesn't mean what you thought it did).

    So why doesn't @a ||= (1..3); work? Well, the definition of ||= implies that such should be the same as @a = @a || (1..3). But you never wrote that as an alternative... because it doesn't work, either.

    @a = @a || (1..3); doesn't "work" because the left side of || is in a scalar context so it is the same as @a = 0+@a || (1..3);. Which means it changes @a when @a isn't empty. (FYI, I'm sure BrowserUk knew this, so I'm just explaining that for other readers.)

    I'm certainly sympathetic to the argument that it makes sense to special-case @a ||= ... so that it will DWYM. I can see somebody arguing against it because they worry it will make it more likely for people to mistakenly write @a || .... Or because it breaks the rules about how ||= was defined.

    I find both sides of that argument roughly equally persuasive.

    My response as to why @a ||= ... doesn't work is simply that nobody has taken the time to special-case it to make it DWYM instead of making it match @a = @a || ...

    I'll suggest the following alternative idiom:

    @a or @a = (1..3);

    Maybe that will lead to somebody adding an or= operator so you can then write @a or= (1..3);?

    - tye        

      That is, people were often asking "defined @a" when they meant "is @a empty". The mistake was so common (you appear to have just made that mistake yourself) that "defined @a" was made to complain.

      If the use of defined on aggregates doesn't match the common expectation; and the actual meaning is non-useful; doesn't it make sense to make it useful by making it match the common expectation?

      That's the question. And I think you've supplied the answer, albeit unknowingly.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        If the use of defined on aggregates doesn't match the common expectation; and the actual meaning is non-useful; doesn't it make sense to make it useful by making it match the common expectation?

        Meh. I see little advantage to providing a second, more verbose, less clear way of getting the exact same answer. I much prefer a compile-time notification of "you put 'defined' in there, but that doesn't do a dang bit of good; maybe you should double check that code or reread some documentation". This author may have been trying for some other meaning, even if such is not the majority case. Why leave a bunch of people scratching their heads about why some places use 'defined' and some don't?

        But, even if such were the chosen design decision, it still would have required that first, the old meaning be deprecated. So the current state is required no matter what.

        - tye        

      Hello tye,

      Thanks for this explanation. When I first read the OP, I completely overlooked the fact that || puts its LHS into scalar context.

      However:

      ... instead of making it match @a = @a || ...

      But it doesn’t match!

      13:08 >perl -MData::Dump -wE "@a = @a || (1 .. 3); dd \@a;" [1, 2, 3] 13:08 >perl -MData::Dump -wE "@a ||= (1 .. 3); dd \@a;" Can't modify array dereference in logical or assignment (||=) at -e li +ne 1, near ");" Execution of -e aborted due to compilation errors. 13:09 >perl -v This is perl 5, version 18, subversion 0 (v5.18.0) built for MSWin32-x +86-multi-thread-64int

      From the error message, it appears that the ||= operator is putting its LHS into scalar context. Am I correct in reading it this way? If so, it seems strange that perlop#Assignment-Operators doesn’t highlight this important semantic difference between X = X op Y and X op= Y?

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Yes, there are some subtle points that I didn't go into.

        First, making @a ||= ... the same as @a = @a || ... would just be leaving a trap. So it is actually a good thing (IMHO) that @a ||= ... is fatal rather than just being identical to @a = @a || .... (And I'm pretty sure that this fatalness was more just a result of implementation details than of extra work to not leave a non-fatal trap.)

        I don't recall the details, but I do recall that there are some subtle differences even between $a = $a || ... vs. $a ||= .... The differences boil down to subtle differences in how $a ||= ... is implemented, and these boil down (IMHO) mostly to matters of efficiency of implementation.

        $a = $a || ... actually deals with $a twice while $a ||= ... deals with $a once.

        So one way to think about why @a ||= ... is fatal is to realize that it isn't just @a = @a || ... but that both of the "@a"s in that expression are dealt with in a single go. So, rather than being like @a = 0+@a || ..., it ends up more like 0+@a = 0+@a || ....

        $ perl -e '@a ||= 1..3' Can't modify array dereference in logical or assignment (||=) ... $ perl -e '0+@a = 0+@a || 1..3' Can't modify addition (+) in scalar assignment ...

        Or you can think of it more like scalar @a = scalar @a || ..., like you guessed.

        If so, it seems strange that perlop#Assignment-Operators doesn’t highlight this important semantic difference

        But it isn't an important semantic difference. It is a subtle difference that mostly doesn't matter. The case where it easily matters is when you try to assign to an array. But that difference isn't "important" because the difference is between something that fails loudly and something that does something you didn't intend.

        op= is only useful on scalars. So there are some subtle details of implementation that are quite difficult to even intentionally arrange for them to matter when using a scalar. And those details are basically due to optimizations, which makes them much more likely to change in future versions of Perl. So these are not the types of things to try to document.

        The thing to document about using op= on an array would be to simply note that it doesn't work. Trying to document inaccurate explanations about subtle implementation details related to that isn't particularly useful.

        - tye        

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (14)
As of 2014-04-16 19:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (433 votes), past polls