Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl Scope Confusion with Format / Write

Why is $var unavailable (out of scope ?) to write when declared with my, if its scope is pretty much package-level ?

package ASDF;
use warnings;
use strict;
use feature 'say';

my $var = 'foo';

format =
@<<<<< @>>>>>
'test : ', $var
.


sub test {
    say $var;
    write;
}


1;

Called with :

perl -wE 'use ASDF; ASDF::test();'

Produces :

foo
Variable "$var" is not available at ASDF.pm line 16.
Use of uninitialized value $var in formline at ASDF.pm line 10.
test :

It appears otherwise available to say in the same scope ...

Replacing my with our fixes it :

foo
test :    foo

Why can't write pick-up on $var correctly ?
Is it a scope issue, or an issue with how Perl's write or format is implemented ?

like image 836
robut Avatar asked Jan 02 '19 18:01

robut


1 Answers

At the bottom of the Perl format documentation it says:

Lexical variables (declared with "my") are not visible within a format unless the format is declared within the scope of the lexical variable.

Reading that would imply that what you are trying would work, but apparently lexically scoped variables work differently for format and write when called from outside of the package they where declared in. Also, all of the examples in the article use global variables...

This more modern tutorial about format repeats that you might run into trouble if you use lexically scoped variables (variables declared with my) because write picks the variables from the current package and, as stated in the comments of your question, was written in a time when Perl did not have the my keyword or lexical scoping.

The solutions the article offers:

When you are ready to output some data, you use write. This design shows the age of formats since write doesn't take arguments to fill in the pictures. It uses the variables that are in scope.

our( $id, $name, $food, $amount ) = qw( 12 Buster Tuna 1.0 );
write();

Formats are also a bit crufty because you don't pass arguments to write to fill in the pictures. Perl relies on variables with the specified names being in scope. You can use lexical variables, but they have to be in the same scope as the format definition, and they have to be in scope when you call write. It's impractical to do that with lexicals, so the most agile way involves localized package variables:

foreach my $record ( @cats ) {
    local( $id, $name, $food ) = @$record;
    write( $fh );
}

And also this advice in the wrap-up:

  • Use localized package variables to set data for the format

So, our and local seem to be the way to go if you want to keep using format and write in modern Perl.

like image 182
Powertieke Avatar answered Oct 13 '22 03:10

Powertieke