Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

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

by isync (Hermit)
on May 15, 2014 at 14:38 UTC ( [id://1086151]=perlquestion: print w/replies, xml ) Need Help??

isync has asked for the wisdom of the Perl Monks concerning the following question:

Since upgrading from Ubuntu 12.04 to 14.04, Perl seems to behave differently.

I've got a script that loads a module, a module where I define the version as our $VERSION = 1.23. Now, when I call print with the $Some::Module::VERSION variable, or when I use it in a concatenation, I get a number as it would be correct for a floating point number within my locale, with a comma - I'm on a German Ubuntu here. But I'd like to have the US dot format...

To illustrate it, I've written this small test script. But: This example script does not work. It prints "1.23" (and, as bonus, tought me that there's a difference between declaring packages in one script and having them loaded as modules. printer() prints "This is .").

#!perl 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"; }
Anyone here who knows what's going on?
I'm using Perl v5.18.2.

Replies are listed 'Best First'.
Re: float: Perl is replacing dot with comma (automagic locale?)
by muba (Priest) on May 15, 2014 at 15:14 UTC

    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.

      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

Re: float: Perl is replacing dot with comma (automagic locale?)
by InfiniteSilence (Curate) on May 15, 2014 at 14:52 UTC

    When you run the code you wrote through the debugger you will notice that it executes the our ... statement after the subroutine has run. Some simple repositioning of code as this,

    #!perl package My::Test; our $VERSION = 1.11; package My::Test::Print; our $VERSION = 1.2; sub printer { print "This is ". $My::Test::VERSION ."\n"; } package main; my $test = 1.23; print "test: $test \n"; print My::Test::Print::printer() ."\n"; 1;

    Makes this output:

    test: 1.23 This is 1.11

    Note that I took the liberty of adding a VERSION in each different package which I think is what you meant to do.

    Celebrate Intellectual Diversity

Re: float: Perl is replacing dot with comma (automagic locale?)
by wjw (Priest) on May 15, 2014 at 14:47 UTC
    First small issue: get rid of the print in print My::Test::Print::printer() ."\n";. That at least gets you the "This is" part of your printer sub to spit out... .

    update: I did the following which seemed to work:

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

    Output: This is 1.11

    test: 1.23

    perl 5, version 19

    ...the majority is always wrong, and always the last to know about it...
    Insanity: Doing the same thing over and over again and expecting different results...
Re: float: Perl is replacing dot with comma (automagic locale?)
by thargas (Deacon) on May 15, 2014 at 18:12 UTC

    Well in general it's better to use a string for the version rather than a floating-point number. Floating-point is not always accurate, and in this case, you'd get what you wanted as a side-effect. I.E. do:

    our $VERSION = '1.2';

    instead of:

    our $VERSION = 1.2;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1086151]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2024-04-24 06:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found