Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Importing a variable into a package

by frazap (Beadle)
on May 15, 2018 at 14:13 UTC ( #1214558=perlquestion: print w/replies, xml ) Need Help??
frazap has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to understand how the import in Text::Template works, and my asking here Re: Text::Template and delayed use vars

I tried this

use strict; use warnings; { no strict "refs"; *T::x = \"a"; *T::y = \"b"; } { package T; print "package ", __PACKAGE__, "\n"; print $T::x, "\n"; print $T::y, "\n"; #print our $y, "\n"; # works, print b print $y, "\n"; # Variable "$y" is not imported } { package P; print "package ", __PACKAGE__, "\n"; print $T::x, "\n"; my $y = "test"; print $y, "\n"; }
What is the trick to have $y displaying the value it received in the main package without giving this variable a package scope using our ?

Thanks

F.

Replies are listed 'Best First'.
Re: Importing a variable into a package (updated)
by haukex (Abbot) on May 15, 2018 at 14:18 UTC

    The trick is that *T::y = \"b"; needs to happen at compile time. Try adding a BEGIN to your first block and your code works. Remember that use Module LIST; is equivalent to BEGIN { require Module; Module->import( LIST ); }.

    Update: I'm not completely sure how you're connecting this question to Re: Text::Template and delayed use vars - could you make that clearer? Without digging into the Text::Template source code, I suspect that something slightly different is going on there. First, note that fully qualified variable names ($T::x) are exempt from strict vars.

    Second, the above statement can be generalized to: Perl needs to know about the variable name before compiling a piece of code. That's why in your example, you need to move the "predeclaration" of $T::y to the compile time before the runtime of the main script by placing it in a BEGIN block (note you could also use the vars pragma, which just hides the same thing behind a nicer syntax).

    But if during the runtime of your main script, you compile and run another piece of code via require, do, or eval, then that code's compile time is later, during the runtime of your script (confused yet? ;-) see BEGIN, UNITCHECK, CHECK, INIT and END). That means the following:

    use warnings; use strict; eval ' package T; print "A: $x\n"; 1 ' or warn "A: $@"; # => Global symbol "$x" requires explicit package name { no warnings 'once'; *T::x = \"foo"; } # runtime of main! eval ' package T; print "B: $x\n"; 1 ' or warn "B: $@"; # => now works

      Thanks for the detail explanation !

      From my tests with Text::Template, I saw that I had to name variables without the package component to catch syntax errors. Having read the docs (sorry, can't find exactly where) this became clearer.

      There is no begin bloc in the loop that gives values to the glob variables Re: Text::Template and delayed use vars as in your last working example

      eval ' package T; print "B: $x\n"; 1 ' or warn "B: $@";

      This seems to be what happen in the module (version 1.47), around line 309 (the print line is mine):

      sub fill_in { ... my $fi_progtext = "package $fi_eval_package; $fi_prepend;\n$fi_lcomment\n$fi_tex +t;"; ... print "eval on \n", $fi_progtext,"\n"; $fi_res = eval $fi_progtext;

      Which prints

      eval on package T; use strict;; #line 1 dokpe.tmpl $annee ; eval on package T; use strict;; #line 4 dokpe.tmpl "\t";
      For a template file starting with
      Plan de fermeture { $annee } Bibliothèque de la Faculté des Sciences (Dokpe) Motif{"\t"}

      So to be sure I understand correctly: since I use eval during the script runtime, I do not need the begin block to assign a value to the glob. In my example above, the begin was needed because the print statement was run during the execution time. Is that correct ?

      Thanks again

      François

        Your understanding is <update> almost correct, just your reason for needing the BEGIN in the root node is a little off, the problem happens at the compile time of the print $y. It's probably best if you look at compile time vs. run time more in general. </update>

        Perl of course has to parse and compile a piece of code like *T::x=\"x"; when it encounters it. However, that piece of code isn't run until runtime, that is, the assignment to the glob doesn't happen until after the entire surrounding code is compiled and while it is being executed.

        So if the assignment is immediately followed by print $x;, when Perl tries to compile that statement, it does not yet know about $x!

        That's why in your example code you need to move the execution of the assignment into the compile time of the surrounding code via BEGIN.

        And in the case of the module, which does indeed appear to use eval: eval STRING needs to compile and run the code it is given, but it does not do so until the eval itself is executed, that is, the runtime of the eval statement. So in this case, if the assignment to the glob happens at run time instead of compile time, as long as it happens before the eval, this will still work, because the assignment is still happening before the compile time of the code that was given to eval.

        Note that this way, with BEGIN and eval in Perl it's possible to basically have as many compile and run times as one wants.

        Also a few updates for clarity.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1214558]
Approved by marto
Front-paged by davies
help
Chatterbox?
Jar. Jar!...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (5)
As of 2018-07-19 19:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?















    Results (417 votes). Check out past polls.

    Notices?