http://qs321.pair.com?node_id=1086161


in reply to float: Perl is replacing dot with comma (automagic locale?)

I don't know about the locale stuff, but I do know why your don't get the version number. Bare with me as I try to pull you step by step.

Compile time and run time

Running a Perl script consists of two phases: a compile phase, in which Perl does things like load modukles, import stuff from them, runs BEGIN blocks, and a whole lot of other things and eventually translates your code to an internal byte code format; and a run phase, in which the generated byte code is actually executed.

When running the code, it pretty much starts at the top of your script and moves down line by line. There are a few exceptions though, because in the compile phase it already collected information about your packages and subroutines and stuff. So if you call My::Test::Print::printer() near the top of your script, perl already knows where the body of this subroutine is because it saw it in the compile phase even though it didn't see it in the run phase yet.

Debugging, step 1: use strict; use warnings;

So what's going on here?

First of all, let's use strict and warnings, just to see if that's going to give us any insight into why perl things it should print the empty string for $My::Test::VERSION.

#!perl use strict; use warnings; my $test = 1.23; print "test: $test \n"; print My::Test::Print::printer() ."\n"; package My::Test; our $VERSION = 1.11; package My::Test::Print; sub printer { print "This is ". $My::Test::VERSION ."\n"; }
Output:
test: 1.23 Use of uninitialized value $My::Test::VERSION in concatenation (.) or +string at x.pl line 17. This is 1

"Use of uninitialized value"? On one hand, this was kind of the warning that I was expecting since it wasn't printing the version number, but on the other hand it had me confused, because I can see the variable initialization just a few lines above!

Eyeball debugging

So I stopped and did a classic run of eyeball debugging. What's that, you ask? Well, you start to play the role of the perl run time phase. Grab a sheet of paper and a pen - you'll be using this to keep track of variables and their values. Basically this sheet of paper is your very own symbol table. Awesome. At the top of this paper, write "SYMBOL TABLE:".

(Edit: (tl;dr: it's not really a symbol table, but I just call it that in this post. Sorry.) Lexical variables (that is, variables declared with my) aren't kept in the symbol table. I was aware of this, but at the time I chose for a simplification of facts and chose to just roll with that term. However, it has been brought to my attention that this simplification may be misleading, for which I apologize.)

Now start at the top of your script, and mentally run the code: look at each line, determine what you think the line does, and if necessary, write down any variable that's created and any value that is assigned, changed, undef'ed, or whatever.

my $test = 1.23;

Mentally run that line and write the appropriate stuff. So you'll have this:

SYMBOL TABLE: $test = 1.23

Still with me? Awesome. Next line.

print "test: $test \n";

Look at your symbol table sheet of paper - see if you have the $test variable there. Do you? What value does it have? What does get printed (not in your mental execution of this code, but what does perl actually print?) Does that match what you have on your symbol table?

Ignoring the decimal comma vs. decimal period (because I don't know why that happens), yes, it does. Wonderful. Let's go on.

print My::Test::Print::printer() ."\n";

To determine what that will print, we'll have to jump to that sub and mentally run that. Let's go.

print "This is ". $My::Test::VERSION ."\n";

Look at your symbol table paper again. See if you have $My::Test::VERSION in there. Do you?

No, you don't. The run time phase hasn't been there yet.

"But if I put my package in its own file and then use it..."

# mytest.pl: #!perl use strict; use warnings; use MyTest; My::Test::Print::printer(); package My::Test::Print; sub printer { print "This is ". $MyTest::VERSION ."\n"; }
# MyTest.pm: package MyTest; our $VERSION = 1.11;
$ perl mytest.pl This is 1.11

How come it suddenly starts working when I move stuff into its own file? Remember what I said near the beginning? In the compile phase, perl loads modules - so by putting MyTest into its own module I've made sure that $MyTest::VERSION gets set before the bulk of my script is executed!

But I don't want to move stuff into separate files!

That's fine, too, but you'll have to tell Perl somehow that it should set the VERSION number before it does anything else. So use a BEGIN block.

#!perl use strict; use warnings; use MyTest; my $test = 1.23; print "test: $test \n"; print My::Test::Print::printer() ."\n"; package My::Test; BEGIN { our $VERSION = 1.11; } package My::Test::Print; sub printer { print "This is ". $My::Test::VERSION ."\n"; }
$ perl mytest.pl This is 1.11

Hope this helps.

Replies are listed 'Best First'.
Re^2: float: Perl is replacing dot with comma (automagic locale?)
by InfiniteSilence (Curate) on May 15, 2014 at 16:33 UTC

    I love this post because it is really detailed. Nice. But using paper to trace code doesn't make sense when you already have the debugger. I mean, technically that is what is is for...

    DB<1> main::(monks1086151.pl:18): my $test = 1.23; DB<1> main::(monks1086151.pl:19): print "test: $test \n"; DB<1> test: 1.23 main::(monks1086151.pl:21): print My::Test::Print::printer() ."\n"; DB<1> This is 1.11 1 main::(monks1086151.pl:23): 1; DB<1> no strict 'refs'; ... DB<8> for (keys %{'My::Test::Print::'}){print qq|$_\t|}; printer VERSION

    Look ma, no paper!

    Celebrate Intellectual Diversity