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

Hi Monks,

I'm trying to use hash slices, but I get a warning saying Scalar value @foo{qw#a b#} better written as $foo{qw#a b#} at -e line 1. - but I don't want a scalar value!

This works OK:

geedorah:/home/wstest# perl -E '$foo{a}=1; $foo{b}=2; say join",", @fo +o{qw#a b#}' 1,2

But with warnings:

geedorah:/home/wstest# perl -wE '$foo{a}=1; $foo{b}=2; say join",", @f +oo{qw#a b#}' Scalar value @foo{qw#a b#} better written as $foo{qw#a b#} at -e line +1. 1,2

But if I take perl's advice, it (unsurprisingly) doesn't work:

geedorah:/home/wstest# perl -wE '$foo{a}=1; $foo{b}=2; say join",", $f +oo{qw#a b#}' Use of uninitialized value within %foo in join or string at -e line 1.

Thanks,

Pete

geedorah:/home/wstest# cat /etc/debian_version 6.0.1 geedorah:/home/wstest# perl --version This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi (with 51 registered patches, see perl -V for more detail) Copyright 1987-2009, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. geedorah:/home/wstest#

Replies are listed 'Best First'.
Re: Incorrect warning when using hash slices?
by toolic (Bishop) on Apr 26, 2011 at 15:27 UTC
    I also confirmed the warning message (I'm on v5.12.2).

    But, it only seems to happen with the # delimiters. If I use other delimiters, I get no warnings:

    $ perl -wE '$foo{a}=1; $foo{b}=2; say join",", @foo{qw{a b}}' 1,2 $ perl -wE '$foo{a}=1; $foo{b}=2; say join",", @foo{qw/a b/}' 1,2 $ perl -wE '$foo{a}=1; $foo{b}=2; say join",", @foo{qw(a b)}' 1,2 $ perl -wE '$foo{a}=1; $foo{b}=2; say join",", @foo{qw#a b#}' Scalar value @foo{qw#a b#} better written as $foo{qw#a b#} at -e line +1. 1,2 $

    Results are similar on v5.8.9 (using print instead of say).

    Update: Here is a quote from qw -> Quote Like Operators:

    A common mistake is to try to separate the words with comma or to put comments into a multi-line qw-string. For this reason, the use warnings pragma and the -w switch (that is, the $^W variable) produces warnings if the STRING contains the "," or the "#" character.

    It is probably best to avoid # delimiters with qw.

      The fact that it warns about "#" in some other context is no reason not to use it.

      Nonetheless, I'd avoid it in case someone allows qw to include comments (although that would requires a use 5.xxx;).

Re: Incorrect warning when using hash slices?
by ikegami (Patriarch) on Apr 26, 2011 at 16:09 UTC

    Definitely a bug.

    $ perl -c -we'my %f; my @a = @f{qw# a b #}' Scalar value @f{qw# a b #} better written as $f{qw# a b #} at -e line +1. -e syntax OK

    Still warns at least as recently as v5.13.10.

    It seems to only warn for some delimiters.

    $ perl -c -we'my %f; my @a = @f{qw( a b )}' -e syntax OK $ perl -c -we'my %f; my @a = @f{qw! a b !}' -e syntax OK $ perl -c -we'my %f; my @a = @f{qw# a b #}' Scalar value @f{qw# a b #} better written as $f{qw# a b #} at -e line +1. -e syntax OK $ perl -c -we'my %f; my @a = @f{qw$ a b $}' Scalar value @f{qw$ a b $} better written as $f{qw$ a b $} at -e line +1. -e syntax OK

    Created ticket RT#89294

      I'm wondering if this relates to perl having difficulty distinguishing between the qw## operator versus a bare hash key that looks like qw##. I guess there's not much to wonder about there. That seems to be what's happening:

      use 5.010_001; use strict; use warnings; my %hash = qw/ one um two dois /; { local $, = "\t"; say @hash{ qw# one two # }; say @hash{ (qw# one two #) }; }

      Output is:

      Scalar value @hash{ qw# one two # } better written as $hash{ qw# one t +wo # } at mytest.pl line 12. um dois um dois

      Only one warning. The second hash slice uses parens to disambiguate the intent of whether qw## represents a hash key, or a qw## operator.

      By the way, if I use diagnostics, I get:

      Scalar value @hash{ qw# one two # } better written as $hash{ qw# one two # } at mytest.pl line 13 (#1) (W syntax) You've used a hash slice (indicated by @) to select a single element of a hash. Generally it's better to ask for a scalar value (indicated by $). The difference is that $foo{&bar} always behaves like a scalar, both when assigning to it and when evaluating its argument, while @foo{&bar} behaves like a list when you assign to it, and provides a list context to its subscript, which can do weird things if you're expecting only one subscript. On the other hand, if you were actually hoping to treat the hash element as a list, you need to look into how references work, because Perl will not magically convert between scalars and lists for you. See perlref.

      ...which seems to support that qr## is being seen by the warnings pragma as a hash key instead of a list constructor. On the other hand, perl still sees it as a list constructor, and does the right thing. I know just about zero with regards to Perl's internal code, but it would seem the culprit is within the warnings mechanism.

      But I'm putting this out there for discussion's sake. I'm as curious as anyone and am just speculating reasons.


      Dave

        I'm wondering if this relates to perl having difficulty distinguishing between the qw## operator versus a bare hash key that looks like qw##.

        B::Deparse to the rescue:

        $ perl -MO=Deparse -wE '$foo{a}=1; $foo{b}=2; say join",", @foo{qw#a b +#}' BEGIN { $^W = 1; } BEGIN { $^H{'feature_unicode'} = q(1); $^H{'feature_say'} = q(1); $^H{'feature_state'} = q(1); $^H{'feature_switch'} = q(1); } $foo{'a'} = 1; $foo{'b'} = 2; say join(',', @foo{'a', 'b'}); -e syntax OK

        So it deparses correctly to @foo{'a', 'b'}

        When I used / instead of # as a delimiter, I get the same deparse, and no warning on running. So I don't see parsing issues as the reason for this bug.

Re: Incorrect warning when using hash slices?
by Anonymous Monk on Apr 26, 2011 at 15:14 UTC
    Confirmed
    $ perl -wle " %foo = 1..4; print for @foo{qw# 1 3 #}; print $^V " Scalar value @foo{qw# 1 3 #} better written as $foo{qw# 1 3 #} at -e l +ine 1. 2 4 v5.12.2 $ perl -wle " %foo = 1..4; print for @foo{q#1#, q#3#}; print $^V " 2 4 v5.12.2
    I've never been tempted to quote with #, never :)