Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Peculiar incident: printing a variable changes its type

by mkhan (Sexton)
on Nov 13, 2013 at 09:08 UTC ( #1062356=perlquestion: print w/replies, xml ) Need Help??
mkhan has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks, I just scratched my head for a good chunk of my time resolving an issue which boils down to this:
use JSON; $params->{timeout} = 2000; $body->{"timeout"} = (defined $params->{timeout}?$params->{timeout} : +3000); print "$body->{timeout}\n"; #<-----suspect line my $body_string = to_json($body); print "$body_string";
In the above code, the mere printing of the hash value adds double quotes to the timeout value in the json as shown in the output.
Output: 2000 {"timeout":"2000"}
Now if you remove the "suspect line" from the above code:
use JSON; $params->{timeout} = 2000; $body->{"timeout"} = (defined $params->{timeout}?$params->{timeout} : +3000); my $body_string = to_json($body); print "$body_string";
Output: {"timeout":2000}
In the second output, the value 2000 wasn't wrapped in quotes. I find it really peculiar as to why the mere printing of the hash value changes the behavior of the output??

Replies are listed 'Best First'.
Re: Peculiar incident: printing a variable changes its type
by tobyink (Abbot) on Nov 13, 2013 at 09:28 UTC

    Strings and numbers in Perl are represented by the same data type: scalars. When the JSON module encounters something that looks like a number, it has to guess whether you wanted to output it as a number or a string. To do this it peeks at the scalar's internals (the SV structure - in particular, its flags) to see whether the scalar has ever been treated as a string. If it has, then it outputs it as a quoted string.

    Install Devel::Peek and note the difference here:

    $ perl -MDevel::Peek -E'my $x = 1; print "$x\n"; print Dump($x)' 1 SV = PVIV(0x864e218) at 0x864d738 REFCNT = 1 FLAGS = (PADMY,IOK,POK,pIOK,pPOK) IV = 1 PV = 0x86483a8 "1"\0 CUR = 1 LEN = 12 $ perl -MDevel::Peek -E'my $x = 1; print $x,"\n"; print Dump($x)' 1 SV = IV(0x86b0724) at 0x86b0728 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 1

    In the first example, the variable $x has been interpolated into a string, and thus acquires a POK flag indicating that the SV structure has a pointer to a string. In the second example, although $x has still been printed out, with a new line afterwards, there was no concatenation; no string operator performed on $x; so it doesn't acquire a POK flag. The JSON module would output this as a number.

    You can make a variable's POK flag disappear by adding 0 to it:

    $ perl -MDevel::Peek -E'my $x = 1; print "$x\n"; $x += 0 ;print Dump($ +x)' 1 SV = PVIV(0x9cba210) at 0x9cb9730 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 1 PV = 0x9cb43a0 "1"\0 CUR = 1 LEN = 12

    Notice that the actual string pointer has been kept (that's the PV line), but it is no longer "OK" - it might have been invalidated by the addition, so the POK flag has gone.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      Install Devel::Peek
      According to corelist, Devel::Peek is a core module since 5.6.0.
      لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Peculiar incident: printing a variable changes its type
by mje (Curate) on Nov 13, 2013 at 09:23 UTC

    Use Devel::Peek and Dump $body->{"timeout"} before and after print. Look at the part called pv. I'm afraid this is what a lot of JSON encoders do, they try to guess whether something is a number by looking to see if your scalar has a pv (has been used in string context) or not. printing your scalar causes the pv to be created.

    Some modules (e.g., DBD::Oracle and DBD::ODBC) have had code added to them to allow an attribute to be specified to remove the pv when fetching numbers from a database but this is XS code. I'm unsure how you do it in pure perl but there is bound to be a way.

Re: Peculiar incident: printing a variable changes its type
by KurtZ (Pilgrim) on Nov 13, 2013 at 14:07 UTC
    print "$body->{timeout}\n"; #<-----suspect line
    avoid stringification!
    print $body->{timeout}, "\n"; # better

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1062356]
Approved by mje
Front-paged by mje
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (6)
As of 2017-10-23 21:57 GMT
Find Nodes?
    Voting Booth?
    My fridge is mostly full of:

    Results (285 votes). Check out past polls.