Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.91k stars 542 forks source link

[feature] Implement `===` operator #18370

Open KES777 opened 3 years ago

KES777 commented 3 years ago

I found only this article: http://www.dlugosz.com/Perl6/web/eqv.html

Describe the solution you'd like Perl comparison is great and save more typing, but when we want specifically compare something with 0 then we should type more, even more:

defined $v  &&  length $v  &&  $v == 0;

Would be nice to just: $v === 0

At the article was mentioned that === is used to compare addresses. For this we can do derefference:

\$x == \$y

so it should not be clash with ===

What do you think about new operator ===?

Thank you.

haarg commented 3 years ago

length is a string operation, but == is a numeric comparison. Why would you check length?

What would this do if applied to strings?

richardleach commented 3 years ago

Would be nice to just: $v === 0

It sounds like you're describing a defined-and operator for numerical comparisons.

defined-and-do-something operators have been discussed before (probably in multiple places). For example, on the P5P mailing list a couple of years ago, a discussion started from a suggestion to simplify things like: if(defined($foo) && $foo =~ /bar/). That particular discussion stalled because adding multiple variants of existing operators seemed undesirable, but unfortunately, no-one suggested a multi-operator syntax that (1) was nice (2) covered enough operators to be worthwhile (3) didn't clash with existing syntax.

=== has the same problem, in that it only helps with one operator.

Also, given that === is used in other languages to force same-type comparisons, @haarg's point is a good one:

What would this do if applied to strings?

(Including if one of $v === $y is a number and the other is a string.)

Grinnz commented 3 years ago

Also complicating any operator like this is that values are not numbers or strings, just used as such by certain operators.

KES777 commented 3 years ago

@haarg : here I use length to escape from situation when ""==0 With my code I try to filter out all values which are undef, empty string, true

@richardleach : What is the best way to find those discussions? May we link those to this ticket? Thank you.

richardleach commented 3 years ago

@KES777 - the specific p5p discussion I mentioned starts at https://www.nntp.perl.org/group/perl.perl5.porters/2019/02/msg253742.html

akarelas commented 3 years ago

I would prefer it if the === operator implemented a functionality similar to this function:

use Scalar::Util 'refaddr';
use experimental 'signatures';

sub eqq ($x, $y) {
    return !defined $y unless defined $x;
    return !!0 unless defined $y;
    return !!0 unless ref $x eq ref $y;
    return length(ref $x) ? refaddr $x == refaddr $y : $x eq $y;
}

This way we would be closer to JS's meaning of === which is (I think) checks whether the two operands are identical.

Benefits:

  1. You don't get warnings when you type if ($x === $y) if either operand is undef.
  2. $x === $y is true if both are undef.
  3. {} === "HASH(0x562c769ce470)" will never return true. Safety.
  4. None of undef === '', undef === 0, and '' === 0 will return true.

A drawback I see is that === will return true if two numbers have the same text representation but differ only by a tiny amount, whereas == wouldn't make that mistake. This is demonstrated in the following example:

my $x = 0.1 + 0.2;  # = 0.30000000000000005, but stringifies to 0.3
my $y = 0.3;        # = 0.3

say int(eqq($x, $y));  # 1
say int($x == $y);     # 0

But maybe that could be solved if we had a separate eqq infix operator that would check for string equality at the end (like the above example), and a === operator to check for numeric equality at the end of the 4th line of the function's body.

I mean something like this:

sub === ($x, $y) {
    return !defined $y unless defined $x;
    return !!0 unless defined $y;
    return !!0 unless ref $x eq ref $y;
    return length(ref $x) ? refaddr $x == refaddr $y : $x == $y;
}
Leont commented 3 years ago

I think it's a terrible idea to introduce an === operator that does something fundamentally different from what it does in other languages, and it's emphatically impossible to make it do the same without a type system.

KES777 commented 3 years ago

@akarelas : Please add for 4 the case: '' === 0 will return false. Also may you please provide an example of drawback

@Leont What fundamentally different? It is like JS: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality

    If the operands are of different types, return false.
    If both operands are objects, return true only if they refer to the same object.
    If both operands are null or both operands are undefined, return true.
    If either operand is NaN, return false.
    Otherwise, compare the two operand's values:
        Numbers must have the same numeric values. +0 and -0 are considered to be the same value.
        Strings must have the same characters in the same order.
        Booleans must be both true or both false.

Perl have next types: string/number, HASH, ARRAY, GLOB. It will be very helpful if this operator will be implemented only for these.

Perl if fundamentally different from other languages: it has no type system. So it will be Ok if === will lack functionality for types also as whole perl does.

KES777 commented 3 years ago

Language popularity is not based on that one language do same as another. Popularity is based on usefulness of language. Personally I like Perl because it has not type system

Here perl get more benefit if we compare values inside hash/array one level deep ( ===* all levels, ===*3 3 level) . To compare addresses we can do usual: $x == $y

this would be useless if === will do same comparison

akarelas commented 3 years ago

Please add for 4 the case: '' === 0 will return false. Also may you please provide an example of drawback

Done (see my edited message above). The example I provided is not a big drawback, since you'd like 0.1 + 0.2 to equal 0.3 anyway. I can't think of a better example at the moment, sorry.

Leont commented 3 years ago

What fundamentally different?

What do you do when one operant is "1" and the other is 1? (note that neither is unambiguously marked as string or number)

richardleach commented 3 years ago

What fundamentally different?

What do you do when one operant is "1" and the other is 1? (note that neither is unambiguously marked as string or number)

It's also unclear to me how this would work with overloading.

For example, given a hypothetical $overloaded object that normally behaves as if it is the number 1:

Whilst the last result might make sense for users who do care about types, it seems inconsistent with the existing operators and would not help users who do not care about the underlying type details.

KES777 commented 3 years ago

'1' === 1 will result to true because values are same. '1.000' === 1 will result to true, because both mean same value Same for overloaded: despite on how it is located at memory it means value 1 so comparison should return true: $overloaded === 1 -- true

=== in compare to == should compare values. Perl is type less so here we should not care about types for ===. Just compare values.

{} === {} -- true [] === [] -- true { a => 1, b => 2 } === { a => 1, b => 2 } -- true [ 0, 3, 7 ] === [ 0, 3, 7 ] -- true [ 0, 3, 7 ] === [ 0, 7, 3 ] -- false ( 7 and 3 are swapped ) "" === 0 -- false undef === 0 -- false undef === "" -- false undef === undef -- true '0' === 0 -- true "" === "" -- true

Or, probably, use same operator instead of ===. Just like PostgreSQL implements IS NOT DISTINCT FROM operator. (Here I mean usage of letters instead of symbols. Now I remembered perls: cmp, eq )

=> select '{"a":2,"b":1}'::jsonb is not distinct from '{"b":1,"a":2}'::jsonb;
 ?column? 
----------
 t
(1 row)

The same operator is not obligated by other languages ;-)

Leont commented 3 years ago

'1' === 1 will result to true because values are same.

Then what about '1.000' === 1 (they are == but not eq)

KES777 commented 3 years ago

@Leont I have updated last my post.

akarelas commented 3 years ago

In my example:

sub eqq ($x, $y) {
    return !defined $y unless defined $x;
    return !!0 unless defined $y;
    return !!0 unless ref $x eq ref $y;
    return length(ref $x) ? refaddr $x == refaddr $y : $x eq $y;
}

...maybe we could replace the last eq with an == only in the case where both $x and $y scalars both have a numeric component. Otherwise do eq like above.