BEGIN block and variable declaration - perl

Is it valid perl to set a variable in a BEGIN block, but declare the variable outside the BEGIN block?
#!/usr/bin/env perl
use strict;
use warnings;
use 5.10.0;
my $var;
BEGIN{ $var = 10 }
say $var;

Yes, it's valid. In fact, you must do it that way, or $var would be local to the BEGIN block and not available in the rest of your program. To quote perlsub:
A my has both a compile-time and a run-time effect. At compile time, the compiler takes notice of it. ... Actual initialization is delayed until run time, though, so it gets executed at the appropriate time, such as each time through a loop, for example.
The compile-time effect is why you can access the variable in the BEGIN block. Be aware that any initialization on the my will take place after the BEGIN block is evaluated (and thus will overwrite any value the BEGIN might set.)

Yes, but you might want to be careful with this pattern, because something very similar will work differently than you might expect:
my $var = 5;
BEGIN { $var = 10 }
say $var; # 5

Related

How to define an environment variable before loading modules?

I use the AnyEvent::DNS module.
I want to disable IPv6, so that the resolver only makes a request for A record.
AnyEvent::DNS, uses the environment variable $ENV{PERL_ANYEVENT_PROTOCOLS}
But setting the variable does not work; the resolver still sends two requests A and AAAA
Code from AnyEvent::DNS:
our %PROTOCOL; # (ipv4|ipv6) => (1|2), higher numbers are preferred
BEGIN {
...;
my $idx;
$PROTOCOL{$_} = ++$idx
for reverse split /\s*,\s*/,
$ENV{PERL_ANYEVENT_PROTOCOLS} || "ipv4,ipv6";
}
How to define an environment variable before loading modules?
Since the code that checks the environment variable is in a BEGIN block, it will be run immediately once the Perl compiler reaches it.
When Perl starts compiling your script, it checks for use statements first. So when you use AnyEvent::DNS, Perl loads that module and parses the file. BEGIN blocks are executed at that stage, while code in methods will only be compiled, not executed.
So if you have something like the following, the code you showed above will be run before you even set that variable.
use strict;
use warnings;
use AnyEvent::DNS;
$ENV{PERL_ANYEVENT_PROTOCOLS} = 'ipv4';
...
There are two ways you can circumvent that.
You can put the assignment in your own BEGIN block before you load AnyEvent::DNS. That way it will be set first.
use strict;
use warnings;
BEGIN {
$ENV{PERL_ANYEVENT_PROTOCOLS} = 'ipv4';
}
use AnyEvent::DNS;
Alternatively, you can just call your program with the environment variable set for it from the shell.
$ PERL_ANYEVENT_PROTOCOLS=ipv4 perl resolver.pl
The second one is more portable, in case you later want it to do IPv6 after all.
Read more about BEGIN in perlmod.

Perl - copy variables value out of BEGIN block

I have a simple script:
our $height = 40;
our $width = 40;
BEGIN {
GetOptions( 'help' => \$help,
'x=i' => \$width,
'y=i' => \$height) or die "No args.";
if($help) {
print "Some help";
exit 0;
}
print $width."\n"; #it is 10 when call with script.pl -x 10 -y 10
print $height."\n"; #it is 10 when call with script.pl -x 10 -y 10
#some other code which check installed modules
eval 'use Term::Size::Any qw( chars pixels )';
if ( $# ) {
if ( $# =~ /Cant locate (\S+)/ ) {
warn "No modules";
exit 2;
}
}
}
print $width."\n"; #but here is still 40 not 10
print $height."\n";#but here is still 40 not 10
I call this script with 2 parameters (x and y), for example: script.pl -x 10 -y 10. But the given values are not saved in variables $width and $height. I want change this variables by giving arguments. How can I copy given values or save them into $width and $height? Is it possible?
EDITED - I added some code to this example
The BEGIN clause is executed before normal code. When you declare $height and $width, you set them to 40 after you process the options.
Solution: process the options outside the BEGIN clause.
The problem is that declaration/definitions like our $height = 40 etc. are executed in two phases. The declaration is performed at compilation time, while the assignment is done at run time. That means something like
my $x = 0;
BEGIN {
$x = 1;
}
say $x;
will display 0, because $x is declared at compile time and set to 1 at compile time because of the BEGIN block. But it is then set to zero at run time.
All you need to do it change the declaration/definition to just a declaration. That way there is no run-time modification of the assignment made by the BEGIN block
use strict;
use warnings 'all';
use feature 'say';
my $xx;
BEGIN {
$xx = 1;
}
say $xx;
output
1
Note that there is no need for our. my is almost always preferable. And please don't use BEGIN blocks to execute significant chunks of code: they should be reserved for preparatory actions of comparable to loading the required modules before run time starts. No one expects a program to output help text if it won't compile, which is what you are trying to do.
All BEGIN blocks are executed in the compile phase, as soon as possible (right after they're parsed) -- before the run phase even starts. See this in perlmod and see this "Effective Perler" article. Also, the declaration part of my $x = 1; happens in the compile phase as well, but the assignment is done at runtime.
Thus the $height and $weight are declared, then your code to process the options runs in its BEGIN block, and once the interpreter gets to the run phase then the variables are assigned 40, overwriting whatever had been assigned in that BEGIN block.
Thus a way around that is to only declare these variables, without assignment, before the BEGIN block, and assign that 40 after the BEGIN block if the variables are still undefined (I presume, as default values).
However, it is better not to process options, or do any such extensive work, in a BEGIN block.
Here are a couple of other ways to do what you need.
Loading the module during compilation is fine for your purpose, as long as you know at runtime whether it worked. So load it as you do, under eval in a BEGIN block, so that you can set a flag for later use (conditionally). This flag need be declared (without assignment) before that BEGIN block.
my $ok_Term_Size_Any;
BEGIN {
eval 'use Term::Size::Any qw(chars pixels)';
$ok_Term_Size_Any = 1 unless $#;
};
# use the module or else, based on $ok_Term_Size_Any
The declaration happens at compile time, and being in BEGIN so does the assignment -- under the conditional if not $#. If that condition fails (the module couldn't be loaded) the assignment doesn't happen and the variable stays undefined. Thus it can be used as a flag in further processing.
Also: while the rest of the code isn't shown I can't imagine a need for our; use my instead.
NOTE Please consult this question for subtleties regarding the following approach
Alternatively, load all "tricky" modules at runtime. Then there are no issues with parsing options, what can now be done normally at runtime as well, before or after those modules, as suitable.
The use Module qw(LIST); statement is exactly
BEGIN {
require Module;
Module->import(LIST);
};
See use. So to check for a module at runtime, before you'd use it, run and eval that code
use warnings 'all';
use strict;
eval {
require Module;
Module->import( qw(fun1 fun2 ...) );
};
if ($#) {
# Load an alternative module or set a flag or exit ...
};
# use the module or inform the user based on the flag
Instead of using eval (with the requisite error checking), one can use Try::Tiny but note that there are issues with that, too. See this post, also for a discussion about the choice. The hard reasons for using a module instead of eval-and-$# have been resolved in 5.14.

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

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.
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.
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".
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.
A sub controls/limits the scope of variables between the braces {} that define its operations. Of course many variables exist outside of a particular function and using lexical my for "global" variables can give you more control over how "dynamic" their behavior is inside your application. The Private Variables via my() section of perlodocperlsub discusses reasons for doing this pretty thoroughly.
I'm going to quote myself from elsewhere which is not the best thing to do on SO but here goes:
The classic perlmonks node - Variable Scoping in Perl: the
basics - is a frequently
consulted reference :-)
As I noted in a comment, Bruce Gray's talk at YAPC::NA 2012 - The why of my() is a good story about how a pretty expert perl programmer wrapped his head around perl and namespaces.
I've heard people explain my as Perl's equivalent to Javascript's var - it's practically necessary but, Perl being perl, things will work without it if you insist or take pains to make it do that.
ps: Actually with Javascript, I guess functions are used to control "scope" in a way that is analagous to your description of using my in sub's.

Initializing global variable on Perl module (with BEGIN block) include

I'm trying to initialize a global variable from a Perl module that has a BEGIN block, but I can't manage to get it work.
This is the Perl module
Package A::B;
our $var;
BEGIN{
$var ||= "/some/default/path";
#create/access files/folders in $var
}
This is my CGI script
use A::B;
$A::B::var = "/correct/path";
But #error returned because $var is not the correct path
The BEGIN block is being executed before the correct path is assigned to $var. Is there a way to work around this without having to remove code from the BEGIN block?
BEGIN { $A::B::var = "/correct/path" }
use A::B;
This answer is unsatisfying to me, but I can't think of any other way, given how your A::B is designed. Ideally a module doesn't depend on who is using it or how often, but this module can really only be used once.

Variable declaration in BEGIN block

In a script like the following, is it possible without dropping 'my' to effectively declare 'var' only once and have it visible outside the BEGIN block?
echo -e "\n\n\n" | \
perl -lne 'BEGIN { my $var="declared & initialized once" } print $var'
Also, why declaring var without 'my' makes it visible outside the BEGIN block?
Place a my $var; before the BEGIN block:
$ perl -le 'my $var; BEGIN { $var = "declared"; } print $var;'
declared
my gives the variable lexical scope, so $var is not defined in your example outside the BEGIN block. Removing the my effectively makes it a global variable, which is accessible across the script after assignment.
Also, why declaring var without 'my' makes it visible outside the BEGIN block?
You're not declaring it then. It is autodeclared as global, if you're not using use strict (which prevents the declaration by default). In a one-liner, strict hurts more than it helps; I'm perfectly fine with not doing a declaration in such a context.