Dart has both:
==
andidentical()
. By the choice of syntax, it feels natural to want to use Dart's ==
operator more frequently than identical()
, and I like that. In fact, the Section on Equality of the Idiomatic Dart states that "in practice, you will rarely need to use" identical()
.
In a recent answer to one of my questions concerning custom filters, it seems that Angular Dart favors use of identical()
rather than ==
when trying to determine whether changes to a model have reached a steady state. (Which can make sense, I suppose, for large models for reasons of efficiency.)
This got me to thinking about identity of int
's and so I wrote some tests of identical()
over int
s. While I expected that small int
s might be "interned/cached" (e.g. similar to what is done by Java's Integer.valueOf()
), to my surprise, I can't seem to generate two int
s that are equal but not identical. I get similar results for double
.
Are int
and double
values being interned/cached? Or maybe identical()
is treating them specially? Coming from a Java background, I used to equate equate Dart's:
==
to Java's equal()
method and identical()
to Java's equality test ==
.But that now seems wrong. Anyone know what is going on?
Numbers are treated specially. If their bit-pattern is the same they must be identical (although it is still debated if this includes the different versions of NaNs).
The main reasons are expectations, leaking of internal details and efficiency.
Expectations: users expect numbers to be identical. It goes against common sense that x == y (for two integers) but not identical(x, y).
Leaking of internal details: the VM uses SMIs (SMall Integers) to represent integers in a specific range (31 bits on 32-bit machines, 63 on 64-bit machines). These are canonicalized and are always identical. Exposing this internal implementation detail would lead to inconsistent results depending on which platform you run.
Efficiency: the VM wants to unbox numbers wherever it can. For example, inside a method doubles are frequently moved into registers. However, keeping track of the original box can be cumbersome and difficult.
foo(x, y) {
var result = x;
while(y-- > 0) {
result += x;
}
return result;
}
Suppose, that the VM optimizes this function and moves result
into a register (unboxing x
in the process). This allows for a tight loop where result
is then efficiently modified. The difficult case happens, when y
is 0. The loop wouldn't execute and foo
would return x
directly. In other words, the following would need to be true:
var x = 5.0;
identical(x, foo(x, 0)); // should be true.
If the VM unboxed the result
variable in the method foo
it would need to allocate a fresh box for the result
and the identical
call would therefore return false
.
By modifying the definition of identical
all these problems are avoided. It comes with a small cost to the identical
check, though.
Seems like I posted too quickly. I just stumbled on Dart Issue 13084: Spec says identical(1.0, 1) is true, even if they have different types which led me to the Dart section on Object Identity of the language spec. (I had previously search for equality in the spec but not object identity.)
Here is an excerpt:
The predefined dart function identical() is defined such that identical(c1, c2) iff:
- c1 evaluates to either null or an instance of
bool and c1 == c2, OR
- c1 and c2 are instances of int and c1 == c2, OR
- c1 and c2 are constant strings and c1 == c2, OR
- c1 and c2 are instances of double and one of the following holds: ...
and there are more clauses dealing with lists, maps and constant objects. See the language spec for the full details. Hence, identical()
is much more than just a simple test for reference equality.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With