Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why declare Perl variable with "my" at file scope?

Tags:

scope

perl

I'm learning Perl and trying to understand variable scope. I understand that my $name = 'Bob'; will declare a local variable inside a sub, but why would you use the my keyword at the global scope? Is it just a good habit so you can safely move the code into a sub?

I see lots of example scripts that do this, and I wonder why. Even with use strict, it doesn't complain when I remove the my. I've tried comparing behaviour with and without it, and I can't see any difference.

Here's one example that does this:

#!/usr/bin/perl
use strict;
use warnings;

use DBI;

my $dbfile = "sample.db";

my $dsn      = "dbi:SQLite:dbname=$dbfile";
my $user     = "";
my $password = "";
my $dbh = DBI->connect($dsn, $user, $password, {
   PrintError       => 0,
   RaiseError       => 1,
   AutoCommit       => 1,
   FetchHashKeyName => 'NAME_lc',
});

# ...

$dbh->disconnect;

Update

It seems I was unlucky when I tested this behaviour. Here's the script I tested with:

use strict;

my $a = 5;
$b = 6;

sub print_stuff() {
    print $a, $b, "\n"; # prints 56
    $a = 55;
    $b = 66;
}

print_stuff();
print $a, $b, "\n"; # prints 5566

As I learned from some of the answers here, $a and $b are special variables that are already declared, so the compiler doesn't complain. If I change the $b to $c in that script, then it complains.

As for why to use my $foo at the global scope, it seems like the file scope may not actually be the global scope.

like image 219
Don Kirkby Avatar asked Jun 20 '14 19:06

Don Kirkby


3 Answers

The addition of my was about the best thing that ever happened to Perl and the problem it solved was typos.

Say you have a variable $variable. You do some assignments and comparisons on this variable.

$variable = 5;

# intervening assignments and calculations...

if ( $varable + 20 > 25 ) # don't use magic numbers in real code
{
    # do one thing
}
else
{
    # do something else
}

Do you see the subtle bug in the above code that happens if you don't use strict; and require variables be declared with my? The # do one thing case will never happen. I encountered this several times in production code I had to maintain.

like image 160
Rob K Avatar answered Nov 15 '22 14:11

Rob K


A few points:

  • strict demands that all variables be declared with a my (or state) or installed into the package--declared with an our statement or a use vars pragma (archaic), or inserted into the symbol table at compile time.

  • They are that file's variables. They remain of no concern and no use to any module required during the use of that file.

  • They can be used across packages (although that's a less good reason.)

  • Lexical variables don't have any of the magic that the only alternative does. You can't "push" and "pop" a lexical variable as you change scope, as you can with any package variable. No magic means faster and plainer handling.

  • Laziness. It's just easier to declare a my with no brackets as opposed to concentrating its scope by specific bracketing.

    {   my $visible_in_this_scope_only;
        ...
        sub bananas {
            ...
            my $bananas = $visible_in_this_scope_only + 3;
            ...
        }
    } # End $visible_in_this_scope_only
    

(Note on the syntax: in my code, I never use a bare brace. It will always tell you, either before (standard loops) or after what the scope is for, even if it would have been "obvious".

like image 34
Axeman Avatar answered Nov 15 '22 13:11

Axeman


It's just good practice. As a personal rule, I try to keep variables in the smallest scope possible. If a line of code can't see a variable, then it can't mess with it in unexpected ways.

I'm surprised that you found that the script worked under use strict without the my, though. That's generally not allowed:

$ perl -E 'use strict; $db = "foo"; say $db'
Global symbol "$db" requires explicit package name at -e line 1.
Global symbol "$db" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.
$ perl -E 'use strict; my $db = "foo"; say $db'
foo

Variables $a and $b are exempt:

$ perl -E 'use strict; $b = "foo"; say $b'
foo

But I don't know how you would make the code you posted work with strict and a missing my.

like image 2
frezik Avatar answered Nov 15 '22 14:11

frezik