7

I made this script to check how scalars change when accidently using 'eq' instead of '==' and vice versa. Using '==' on strings changes nothing but using 'eq' on numbers makes the scalar change somehow. Here comes the code:

#!/usr/bin/perl
use strict;
use JSON;

my $str = "123";
my $num = 123;

print "BEFORE:\n";
print "str: ", \$str, " num: ", \$num, "\n";
print to_json({str => $str, num => $num}, {pretty => 1});

if ($str == 23) { }
if ($num eq "123") { }

print "AFTER:\n";
print "str: ", \$str, " num: ", \$num, "\n";
print to_json({str => $str, num => $num}, {pretty => 1});

print "\n";

Output:

BEFORE:
str: SCALAR(0x8010f8) num: SCALAR(0x801050)
{
      "num" : 123,
      "str" : "123"
}
AFTER:
str: SCALAR(0x8010f8) num: SCALAR(0x801050)
{
      "num" : "123",
      "str" : "123"
}

With words, $num is changed from being a number to being a string. By commenting the line

if ($num eq "123") { }

$num is not changed anymore. Is this a bug or feature? Why does this happen? Also, how can I see this without using to_json?

perl --version
This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi
  • 2
    http://stackoverflow.com/a/12693984/223226 – mpapec Jan 08 '14 at 10:12
  • possible duplicate of [How to tell apart numeric scalars and string scalars in Perl?](http://stackoverflow.com/questions/12686335/how-to-tell-apart-numeric-scalars-and-string-scalars-in-perl) – amon Jan 08 '14 at 11:25
  • I wouldn't say this question is a duplicate of stackoverflow.com/a/12693984/223226 . Sure, that question answers how I can see the type without using to_json but my real question here is if the result of my code is a bug or a feature, e.g. why the eq operator changes the type of the argument. – Manne Tallmarken Jan 08 '14 at 11:47
  • 2
    Numeric isn't a type in Perl, it's a context. Most likely Perl caches and prioritises contextual data associated with the scalar value (you can use `Devel::Peek` to get some more information about what is happening to the scalar). As to what the rationale/design is here, I have no clue. Partly this is a feature of `to_json`, not Perl - it has to take hints from whatever it can find, and if a scalar has been treated as both a number and a string it is getting confused (or playing it safe) – Neil Slater Jan 08 '14 at 12:09

1 Answers1

6

Perl variables are multivalued, e.g. they have slots for string, integer, floating point and reference.

If you assign a string to a variable it will be stored in the string slot and this slot will be marked as valid. If you later access the variable as a number, the string will be converted and the floating point or the integer slots will be updated and marked valid. In your example $str will initially have a valid string slot and after the comparison with an integer it will also have valid integer slot. If you would have compared it with a float it would have a valid float slot instead. You might check this with Devel::Peek::Dump: POK means valid string slot, IOK is a valid integer slot and NOK a valid float slot.

Similar things happen, if you store an integer and later use it as a string. In your example $num will have initially a valid integer slot (IOK) and once you access it as a string (by using eq) it will be converted to a string and the string slot will be filled and be valid (POK) additionally to the integer slot.

to_json probably just looks at the variable and takes the first valid slot it finds, starting with the string (I guess this must be encode_sv in JSON::XS - this checks in this order: string, float, integer). So if the string slot is valid it will be printed with quotes around it.

Other languages (like python) don't have this multi-slot thing, they just have a single type and if you use the variable in another context it will croak. Both ways have their pros and cons :)

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172