[Perl 6 page]

eqv, ===, =:=, etc.


Perl 5 and previous have different operators for eq and ==, and that is easy to understand. With no types on the variables, you need different operators to specify string or numeric comparisons. Perl 5 has objects and classes, and there is no real typed comparison that works between objects of arbitrary type, so something is lacking.

But Perl 6 offers no less than eq, ==, eqv, ===, =:=, and ~~. Most of them are straightforward, and are presented in the following summary. But === vs eqv is not explained well in S03 version 135, and needs explaining.


eq ne !eq leg lt le gt ge String Value Comparisons
== != !== <=> < <= > >= Numeric Value Comparisons
~~ !~~ Smart Match
=== !=== Object Identity
eqv !eqv cmp after !after before !beforeCanonical Equivalence
=:= !=:= Container Identity

operators eq and ==

If you know Perl 5, you already know that string and numeric tests have different operators. If you are new to Perl, here is the issue:

my $x = 123;
my $y = "123.0";
if $x == $y { say "Numbers match." }
if $x eq $y { say "Strings match." }

With untyped variables, especially when you are playing fast and lose with the difference between formatting and computational values in the first place (Perl was made for parsing and printing reports), it is important to get the right kind of comparison. In this example, the comparison is true for numbers but false for strings.

In Perl 6, these are still with us. That’s why we can’t use == for a more general form.

operator ~~

The ~~ operator is for smart matching. Here are some examples of what you can do with it:

$x ~~ 1..10   # $x within range
$x ~~ %hash  # $x is a key in the hash
$x ~~ /\d+/  # regular expression

The smart match can be used for many things, and is defined between a great variety of different argument types.

operator =:=

The last one that is simple to explain is =:= which tests for container identity. It tells you whether two names are aliased to the same symbol. That’s all it is for, and since aliasing is done with := or ::= it is easy to keep straight.

operator eqv

The eqv operator is for testing canonical equivalence. This is what you think of as “equal” in C++. If you want to test two Dog objects to see if they hold the same value, this is the operator to use. When writing a Dog class, define infix:<eqv> to indicate equivalence, in the same manner as operator== in C++.

If you don’t define such an operator for your class, the default is to compare all the attributes. Again, this is like the case with C++’s operator==.

If you compare things of two different types using eqv, you get whatever the multi dispatch figures out needs to be called, assuming it is not ambiguous. Again, this is just like C++ does with overloading, only Perl does it at run time rather than compile time.

The old-style Perl ≤5 operators == and eq can be described in terms of eqv and forcing the right type. Since + forces numeric context and ~ forces string context, we have $a == $b to be the same as +$a eqv +$b, and $a eq $b to be the same as ~$a eqv ~$b.

If you were using typed variables or indicating context on the items, you could just use eqv for numbers and strings too. It is a different philosophy to keep track of the proper types of things rather than to treat them in different ways based on context with no clear idea of which type it actually is at the moment.

my Num $x = get_input();
my Num $y = 2 * $z;
if $x eqv $y { ... }

Even though get_input in this example returns a string, I’m clear that I wanted a Num and make sure it becomes one before continuing. So the test is not ambiguous, but clearly a test between two numbers.

In short, eqv is the Perl 6 equivalence operator, and is most similar to operator== in C++ which compares values, but not like == in Java or C# for reference types.

my Dog $d1 .= new("Fido", :breed<Aussie>);
my Dog $d2 .= new("Rex", :breed<Aussie>);
fail if $d1 eqv $d2;
$d2.name = "Fido";
if $d1 eqv $d2 { say "Dogs test as equivalent" }

Within the Dog class, the eqv operator tests that all the attributes are the same, so it becomes True as soon as the attributes are changed so that they do in fact match. But $d1 and $d2 are indeed different instances of Dog. The eqv operator tests if the values (or contents) are equivalent.

operator ===

The === operator checks to see if the two arguments are the same object. This is like comparing pointers in C++, or comparing reference types in Java and C#.

Given the Dog example above, $d1 === $d2 will always be False, because they are different instances, created by different calls to new, regardless of whether they happen to have the same values for their attributes.

Clearly, an implementation of eqv can call === first and not bother comparing all the attributes if it discovers that both parameters are the same object.

If you are not confused, stop now because the confusing part is coming up.

The complication here is that the concept of object identity is more complicated than just comparing addresses like with C++ class instances. This is actually meant to improve matters over the case of Java and C# which has stark differences in semantics between reference types and value types.

In Perl 6, the concept of a value type or immutable type is to tie the object’s identity with its value. This is the case with numbers and strings, and is a pragmatic matter with the design of Java. But in Perl 6, everything is an object, and it is important to generalize the concept and improve upon the situation and still allow the compiler to optimize values in a particular way.

By default, the object id is the address, or whatever implementation detail holds that kind of concept. But, a class can define things so that representations at different memory locations return the same id. For numbers that tend to live in registers or get a more streamlined representation than a full object record, the concept of an address does not always exist.

For a type that is made to behave this way, two different instances will compare as the same object if they have the same value. If the object is not constant, this can be very confusing if you change “it” and it doesn’t change everywhere. So don’t do that. Value types are for numbers and strings and constant singletons.

But, because Num, Int, Str, etc. are indeed value types, you can use === on them and think you are comparing values. For these types, the value is the identity, so it works with either operator exactly the same. I advocate using eqv for its intended purpose as canonical equivalence, and not use === in those cases where they happen to mean the same thing just because it’s prettier. This will only confuse you when you need to compare Dogs or other types. Always use eqv to test whether two values are the same, and always use === to see if you have two references to the same object.