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

From the I learn new things about Perl every day department.

On the Paris.pm list today, someone wrote (translated):


> my %opts;
> getopts('hf:d:', \%opts);

my() is an operator like any other, syntactically speaking:

    getopts('hf:d:', \my %opts);

Since a function call doesn't define a new scope (until one enters the function), the above works just fine and the code snippets appear to be functionally equivalent, though they deparse slightly differently:

$ perl -MO=Deparse -e 'foo(\my $bar)' foo \my($bar); -e syntax OK $ perl -MO=Deparse -e 'my $bar; foo(\$bar)' my $bar; foo \$bar; -e syntax OK

I was pleasantly surprised by this and sort of like the idea of defining an variable like this, but I suspect that this might be obfuscated and potentially bug prone. For example, the following code might look like both hashes should have the value of 2 for the key 'foo'. They don't.

#!/usr/bin/perl use strict; use Data::Dumper; my %hash1 = ( foo => my ($bar) ); $bar = 2; my %hash2 = ( foo => \my $bar2 ); $bar2 = 2; print Data::Dumper->Dump([\%hash1,\%hash2], [qw(*hash1 *hash2)]);

The first hash has undef for the value of 'foo' because my appears to return what it's declaring. The second hash has a reference to the value of $bar and by dereferencing it, you can get to the value. Thus, if you take a reference to my, you get a reference to the declared variable and if you're declaring a list, you get a reference to each item of the list.

my $ref = \my $foo; printf $ref eq \$foo ? "Same address\n" : "Not same address\n"; my @array = \my ($this, $that); $this = "Ovid"; print Data::Dumper->Dump([\@array,$this,$that], [qw(*array *this *that +)]);

Is there a particular use for this other than obfuscation and are there any 'gotchas' that one should be aware of?

Cheers,
Ovid

New address of my CGI Course.
Silence is Evil (feel free to copy and distribute widely - note copyright text)

Replies are listed 'Best First'.
Re: Referencing built-ins
by dragonchild (Archbishop) on Apr 08, 2003 at 20:25 UTC
    Assuming the above syntax works, this makes bind_columns() a lot easier to work with.
    .... my $sth = $dbh->prepare_cached(...) || die ...; $sth->execute(@some_values) || die ...; $sth->bind_columns(\my ($foo, $bar, $baz)); while ($sth->fetch) { # Use $foo, $bar, and $baz here. } $sth->finish; ###### Used to be: ... my ($foo, $bar, $baz); $sth->bind_columns(\($foo, $bar, $baz)); ...
    ++!

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Referencing built-ins
by jmcnamara (Monsignor) on Apr 08, 2003 at 21:37 UTC

      What's really interesting about your first link is that I apparently upvoted that a couple of years ago, but simply forgot that you can do this! I can't help but wonder how many things about Perl that I forget simply because I don't use them every day.

      Cheers,
      Ovid

      New address of my CGI Course.
      Silence is Evil (feel free to copy and distribute widely - note copyright text)

Re: Referencing built-ins
by grinder (Bishop) on Apr 08, 2003 at 23:08 UTC

    (I didn't know you were subscribed to paris-pm-list :)

    It wasn't just "someone", it was Rafael Garcia-Suarez, p5p summarizer for the rest of us mere mortals who can't keep up with p5p.

    The rest of the thread was interesting too, and adds context to what's being discussed here.

    It started out when a person who had use strict enabled wondered why the following code didn't work (it complains about $opt_h being undefined):

    getopts("hf:d:") || die HELP(); if ($opt_h) { HELP(); }

    A couple people pointed out that an alternative consists of passing getopts a reference to a hash, which sidesteps the issue.

    To get the code to work as advertised you need to predefine some package variables, such as with

    use vars qw/$opt_h $opt_f $opt_d/;

    I personally took/take issue with using the hash approach, because if you screw up your keys, Perl will obligingly autovivify them for you. I once spent far too long tracing down a bug because I missed this fact. I was staring at code that should have been executed, but it wasn't, because the condition was if( $opt{t} ) instead of if( $opt{T} ) or something along those lines.

    These days I prefer to encapsulate that stuff so that the compiler will grunt at compile time if I make a misteak:

    { my %opt; getopts("hf:d:") || die HELP(); sub opt_h { exists $opt{h} ? 1 : 0 } sub opt_f { exists $opt{f} ? 1 : 0 } sub opt_d { exists $opt{d} ? 1 : 0 } } if( opt_h ) { ... } if( opt_j ) { # compile time error ... }

    <update>

    And graff is correct about how to deal with options that have values. You can add all sorts of functionality in those subs. You can even stick prints in them to find out where in a twisty maze of code they get called :)

    In this particular instance (I didn't play close enough attention to the getopt spec in the OP; what you see is a cut and paste error) would be something more like:

    sub opt_f { if( not exists $opt{f} ) { 0 } elsif( not defined $opt{f} ) { 'default-value' } else { $opt{f} } }

    The idea being that you never return undef, and you return different things according to whether the switch was not specified, or specified with(out)? an optional value.

    </update>

    What is more, %opt is now hidden. You can no longer accidently stomp on it and accidently change the directives your program is supposed to follow. I've done that too in my time.

    Rafael's reply is to be taken within the context of that code snippet. Personally I think it's interesting that Perl allows that, however, I can't help but feel that it's not overly useful, because it buries the declaration deep inside a statement, and it might be hard for someone to spot.

    As it is, the following still gives me the willies:

    (my $new = $old) =~ s/foo/bar/;

    I can't help thinking that $new is suddenly going to go out of scope outside the parentheses. I know it won't, and I use this idiom frequently, but I don't think I'll ever be truly satisfied with it. That said, I like the functionality, I just think it's a reflection on the botched state of precedence rules.

    _____________________________________________
    Come to YAPC::Europe 2003 in Paris, 23-25 July 2003.

      I'm going to try to remember this trick:
      { my %opt; getopts("hf:d:") || die HELP(); sub opt_h { exists $opt{h} ? 1 : 0 } sub opt_f { exists $opt{f} ? 1 : 0 } sub opt_d { exists $opt{d} ? 1 : 0 } }
      But if a given option takes an argument, wouldn't this be the way to go:
      { my %opt; getopts("hf:d:") || die HELP(); sub opt_h { exists $opt{h} ? 1 : 0 } sub opt_f { exists $opt{f} ? $opt{f} : '' } sub opt_d { exists $opt{d} ? $opt{d} : '' } } if ( opt_f eq "foo" ) { ... } # usw, etc.
      There's just that little issue about remembering to leave off the $ sigil when testing the options -- some habits are hard to unlearn (but "use strict" will cover that).
      Excellent technique. I wonder though, shouldn't that be as follows?
      sub opt_h { exists $opt{h} } # boolean sub opt_f { exists $opt{f} ? $opt{f} : return } # takes value

      Makeshifts last the longest.

Re: Referencing built-ins
by Anonymous Monk on Apr 14, 2003 at 05:49 UTC
    This trick has been used before in the monastery. For instance Class::FlyweightWrapper used it. At a guess, the only reason why it isn't better known is that people generally don't have a driving need for convenient scalar references.
Re: Referencing built-ins
by John M. Dlugosz (Monsignor) on Apr 09, 2003 at 19:36 UTC
    I have not explored it in detail, but I do write
    read (FH, my $buffer, 999999)
    to define this commonly-used "out" parameter right where it's first used. I figured this is a general case.

    So, it works fine when the function is "pass by reference", and if it does something else when you use \ to take a reference any difference must be due to parsing issues.

    —John