9

In this code snippet:

    use strict;
    use warnings;
    use Data::Dumper;
    my $r = [qw(testing this thing)];

    print Dumper($r);
    foreach my $row (@({$r})
    {
        print "$row\n";
        $row .= 'mod';
    }
    print Dumper($r);
    print Dumper(@({$r});

I figured out that the '(' after the '@' in the foreach is causing this not to loop correctly. I have no idea why this code even works as there is no ending parenthesis. What is this doing? It looks to be creating a new variable on the fly, but shouldn't 'use strict' have fired or something?

Please help explain what that '@(' is doing and why it still runs without an ending parenthesis.

  • That is really odd. I had a theory that it is somehow referencing the built-in variable `$(`, and that `@({$r}` somehow makes perl try to make some strange referencing. Curiously, you can print many such "variables", e.g. `@,`, `@/` without an error, and they show an empty list. – TLP Nov 22 '11 at 01:18

4 Answers4

8

That is a hash slice of the %( variable, which being part of the *( glob, is exempt from strict vars. This is true for variables that Perl has predefined, in this case $( and also for all of the other glob slots for the names of punctuation variables. All punctuation variables are global across all packages, and their fully qualified names are the short forms: $), @), %), &)... Since strict 'vars' does not apply to fully qualified names, none of these names are errors.

Expanding a bit:

@({$r};
@{(}{$r};
@{'main::('}{$r};  # needs strict refs to be off

Those lines are all equivalent.

With use warnings; perl will let you know that it would be better to write a slice of a single value with a $ sigil:

$({$r};
${(}{$r};
${'main::('}{$r};  # needs strict refs to be off

Which in trying to resolve a typo would have pointed you in the right spot. Which is why you should always use both warnings and strictures.

For more detail, the perlvar manpage shows all of the punctuation variables with at least one sigil or another. And if you wanted a reference about the scoping of punctuation variables, the package docs have that.


All the punctuation variables are also immune to used only once warnings, and that might be a bug...

Eric Strom
  • 39,821
  • 2
  • 80
  • 152
  • This variable is not in perlvar. Where is it from? Is it like Keith says, that perl just assumes punctuation characters are pre-defined? – TLP Nov 22 '11 at 01:40
  • It might be a symbolpunctuation variable, but it's not one of Perl's variables. It seems that no punctuation variable triggers a strict error, even the ones Perl doesn't create. – ikegami Nov 22 '11 at 01:44
  • @ikegami Sounds like a shortcoming of the `strict` module. – TLP Nov 22 '11 at 01:49
  • `$ü`, `$ö`, `$ä` etc seem to also be exempt from stricture. – TLP Nov 22 '11 at 02:00
  • since `*(` is global to all packages, resolving any variable in this glob directly is a fully qualified name already without any further disambiguation. strict 'vars' does not apply to fully qualified names. perhaps that was the logic used... – Eric Strom Nov 22 '11 at 02:01
  • @TLP => the utf8 characters are caught by strict vars in the copy of 5.12.3 that I have here, or at least the first one is. – Eric Strom Nov 22 '11 at 02:07
  • @Eric Wouldn't the OP have gotten a warning then? The code does have `use warnings`. – MetaEd Nov 22 '11 at 02:35
  • @MetaEd => it seems the punctuation globs are also immune from "used only once" warnings. as written the OP would have gotten the warning I described in my answer. – Eric Strom Nov 22 '11 at 03:55
  • @Eric Strom, Saying `@(` is fully qualified is wrong. It's fq name is `@::(` (aka `@main::(`). `@Foo::(` is a different variable. What makes punctuation variables global is that Perl will look for them in `main::` instead of in the current package if you *don't* fully qualify them. So that's not it either. – ikegami Nov 22 '11 at 07:19
  • @ikegami => None of the examples you gave parse in perl as written. They are ALL syntax errors. `@(` is the name of the variable, and it is automatically qualified into main::, you can't write any of the other forms without resorting to some sort of quoting mechanism beyond perl's interpretation of a syntax level identifier. (`our` can of course be used to lexically redirect the qualification, but there is still no other way to spell a full qualification of a specific package's `(` that is strict vars compliant (apart from the usual diving into %::)) – Eric Strom Nov 22 '11 at 07:43
  • @Eric Strom, "They are ALL syntax errors." How weird! It is the case for other superglobals ($::_, $::1, etc) Wow, so many corner cases. But that just goes to show you still haven't found an accurate explanation. Really, I don't think there is one. I believe the are bugs, and any attempt to explain them are jsut false rationalisations. – ikegami Nov 23 '11 at 02:02
3

@({$r} is a hash slice (documented in perldata) of the hash %(.

%h = (a=>1, b=>2, c=>3);
say for @h{qw( a c )};  # 1 3

Perl itself doesn't use %(, so it was surely empty.

You surely meant to use @{$r}, a rather complex way or writing @$r.

ikegami
  • 367,544
  • 15
  • 269
  • 518
2

When I run perl -cw on it, it says:

Scalar value @({$r} better written as $({$r} at tmp.pl line 7.
Scalar value @({$r} better written as $({$r} at tmp.pl line 13.

$r is a reference to an array. As Eric Strom just posted, @({$r} is a hash slice of the %( hash variable.

You never declared %(, and it's not one of the predefined variables listed in perldoc perlvar. So why doesn't use strict; use warnings; cause Perl to complain about it? It probably just assumes that any variable whose name is a punctuation character is predefined (simpler than keeping track of which ones really are, some of which might be undef anyway).

Note that $( is a valid predefined variable (it's the real group-id of the current process), so something that looks like a mismatched parenthesis isn't necessarily an error.

It looks like it's simply a typo that, for obscure reasons, Perl didn't complain about.

Change @({$r} to @{$r} to do what you (presumably) actually meant to do.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

It didn't click for me, that this is a hash slice, until I ran it through B::Concise

$ perl -MO=Concise junk 
    Scalar value @({$r} better written as $({$r} at junk line 7.
    Scalar value @({$r} better written as $({$r} at junk line 13.
    junk syntax OK
    1l <@> leave[1 ref] vKP/REFC ->(end)
    1     <0> enter ->2
    ...
    ...
    1h             <@> hslice lKM ->1i
    1d                <0> pushmark s ->1e
    1e                <0> padsv[$r:335,339] l ->1f
    1g                <1> rv2hv[t17] sKR/3 ->1h
    1f                   <#> gv[*(] s ->1g
    -              <1> ex-rv2cv sK/2 ->-
    1i                <#> gv[*Dumper] s ->1j

hslice means hash slice :) get to know your B:: tree, its really helpfull

gangrene
  • 21
  • 1