4

In Perl, I know of three ways to test objects for equality: ==, eq, and ~~. All of these tell me that 1 is equal to "1" in Perl 5.

However, 1 and "1" are not the same thing. How can I compare two objects so that 1 equals 1, "1" equals "1", 1 does not equal "1" or 2, and "1" does not equal "01"? Answers for both Perls would be appreciated.

ikegami
  • 367,544
  • 15
  • 269
  • 518
Pavel
  • 1,024
  • 1
  • 13
  • 20
  • 9
    In perl a variable can be `1` and `"1"` at the same time. You're trying to make a distinction that doesn't really exist. – AKHolland Feb 12 '18 at 15:48
  • you can have a look at the answer here: https://stackoverflow.com/questions/21070947/when-does-perl-impose-string-context – Flying_whale Feb 12 '18 at 15:55
  • See also [How to tell apart numeric scalars and string scalars in Perl?](https://stackoverflow.com/questions/12686335/how-to-tell-apart-numeric-scalars-and-string-scalars-in-perl) – haukex Feb 12 '18 at 22:30
  • 1
    also https://stackoverflow.com/questions/12647/how-do-i-tell-if-a-variable-has-a-numeric-value-in-perl/3806159#3806159 – ysth Feb 12 '18 at 22:37

3 Answers3

11

Don't. In Perl, one is one. Polymorphism based on a value's type is bound to fail. That's why Perl has two comparison operators, and that's why ~~ is broken[1].

For example, the scalar returned by !0 contains three values, one stored as an integer, one stored as a floating point number, and one stored as a string.

For example, an object of class package Foo; use overload '0+' => sub { 1 }, fallback => 1; might not contain one at all, but it's considered to be one in numerical and string contexts.


  1. That's why it's still flagged as experimental.
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Thanks a lot. I feel like I understand Perl so much more now. I only mentioned `~~` because I've been trying to use Perl 6 more than Perl 5 recently. – Pavel Feb 12 '18 at 16:01
  • 1
    I don't know Perl 6, and the answer could be completely different. That's why I removed the Perl 6 portions of your question. Feel free to ask the same question about Perl 6 as a separate question. – ikegami Feb 12 '18 at 18:12
  • 1
    one is not always one: `perl -E'say( (1 & "") eq ("1" & "") ? "eq" : "!eq")'` – ysth Feb 12 '18 at 22:49
  • @ysth, Yes, that operator breaks the rule, but doing so has caused countless problems. This reinforces my answer. – ikegami Feb 13 '18 at 17:31
1

Serializers like Data::Dumper::Dumper and JSON::encode_json can treat scalars internally stored as numbers differently from scalars internally stored as strings, so you can compare serialized output:

use Data::Dumper;
$x = 1;
$y = "1";
$same = Dumper($x) eq Dumper($y);
mob
  • 117,087
  • 18
  • 149
  • 283
  • 2
    This can lead to different behaviour in different Perl versions, though, if you do more complex stuff with the variables before dumping them. – choroba Feb 12 '18 at 16:46
  • I agree with @choroba, just using a string in a numeric context can change what `Data::Dumper` outputs. `perl -MData::Dumper -e 'my $x="1"; print Dumper($x); $x==1 and print Dumper($x)'` prints `$VAR1 = '1'; $VAR1 = 1;` – haukex Feb 12 '18 at 21:24
  • Most users, most of the time, should not worry about the internal representation of their scalars or rely on them having any particular internal representation. For whatever reason -- maybe a good reason, maybe not for a good reason -- OP is worrying about it, and looking at Dumper output is one of the ways to access it. – mob Feb 12 '18 at 21:59
  • @mob I completely agree that users normally shouldn't have to worry about the internal representation, and this kind of question usually comes up when someone is either trying to implement a Dumper themselves, or when they're looking at the wrong kind of solution for their actual problem. However, I disagree that *"looking at Dumper output is one of the ways to access it"*, for the simple reason I showed above ([see also](https://stackoverflow.com/questions/12686335/how-to-tell-apart-numeric-scalars-and-string-scalars-in-perl)). Often, `Scalar::Util::looks_like_number` can be a good solution. – haukex Feb 12 '18 at 22:36
  • 1
    They "address" the issue by imposing an arbitrary set of rules that results in values that are different than they might appear in Perl. e.g. `perl -MJSON -le'$!=5; print JSON->new->encode([$!]); print $! == 5;'` – ikegami Feb 13 '18 at 17:34
1

You can use this snippet as a starting point for your comparison function:

my $num = 42;
my $str = "42";
say B::svref_2object(\$num)->FLAGS;
say B::svref_2object(\$str)->FLAGS;

You will see that the flags of both variables differ. Read perldoc B for more in-depth information.

You may also find the source code of JSON::PP interesting. Search for "sub value_to_json".

Guido Flohr
  • 1,871
  • 15
  • 28
  • Fails for magic vars (e.g. `${\substr("abc",1,1)}`, initially). – ikegami Feb 13 '18 at 17:20
  • Fails for overloaded objects. – ikegami Feb 13 '18 at 17:24
  • Doesn't address scalars that contain more than one of the following: a string, an integer, and a floating point number. (e.g. `!1`). – ikegami Feb 13 '18 at 17:26
  • *starting point* ... I will update the answer and point the OP to JSON::PP which addresses a lot of the issues you mentioned. – Guido Flohr Feb 13 '18 at 17:31
  • It "address" the issue by imposing an arbitrary set of rules that results in values that are different than they might appear in Perl. e.g. `perl -MJSON::PP -le'$!=5; print JSON::PP->new->encode([$!]); print $! == 5;'` – ikegami Feb 13 '18 at 17:34
  • There are two equality testing operators in Perl plus the experimental one. And if that is not sufficient for a specific use case, then `B::svref_object` helps you checking more conditions. And it answers the question how you can distinguish "1" from 1 without going through `Data::Dumper` in most cases. If that is enough or not depends on the exact use case. – Guido Flohr Feb 13 '18 at 17:46
  • Re "*B::svref_object helps you checking more conditions.*", Then please show how. What you posted in your answer doesn't cut it, as previously mentioned. – ikegami Feb 13 '18 at 17:47
  • A real problem that I had solved with B::svref_2object: I had to reimplement a little JavaScript template engine in Perl and it was crucial that they produced the same output for the same input JSON (generated by Perl). The problem was to decide whether "1 + 1" should render as "11" or "2" and looking at the flags in Perl was good enough for that. – Guido Flohr Feb 13 '18 at 17:54
  • The gaps in your testing is not interesting to me. – ikegami Feb 13 '18 at 17:58