What is the difference between =
and ==
in Pharo Smalltalk? What are they named, one isEqual
and the other?
= ~= equality / inequality (deep)
== ~~ equality / inequality (shallow)
What is the difference between =
and ==
in Pharo Smalltalk? What are they named, one isEqual
and the other?
= ~= equality / inequality (deep)
== ~~ equality / inequality (shallow)
One important thing to take into account is that =
is not only used explicitly when your code compares two objects. The message =
is also implicitly used by many other messages such as: includes:
, <=
, >=
, remove:
, keyAtValue:
, indexOf:
, upTo:
, peekFor:
, occurrencesOf:
, add:
(in Set
), at:
(and friends at:ifAbsent:
, at:ifAbsentPut:
, etc.), and many others.
This means that when you redefine =
in your class you should make sure that
=
is robust (e.g., banana = car
gives no error) and=
you also redefine hash
.The reason for the first condition is to be able to compare any two objects without the sender having to take any special care before sending the =
message.
The reason for the second is that if in the future your objects are used in a hashed collection (Set
, Dictionary
, Bag
, etc.) they will honor the important invariant required by these entities
IF a = b THEN a hash = b hash
And given that it is a very good practice to make sure that hash
values are SmallIntegers
, one could say
IF a = b THEN a hash == b hash
In other words, every time two objects are considered to be equal their hash
values should be granted to be identical.
Naming
When reading an expression involving =
such as a = b
one says a
is equal to b
or a
equals b
.
When reading a == b
Smalltalkers say a
and b
are the same object, or a
is identical to b
or even a
is equal-equal to b
.
Further comments
The =
message is domain specific, meaning that it is up to you to decide when two Smalltalk objects represent the very same object in your application.
The ==
message is a system feature, meaning that it is implemented by the Virtual Machine (VM) which verifies that the objects being compared occupy the very same location in memory. In other words, two variables a
and b
are equal-equal when they are bound to the very same object.
Examples
a := 'This String'.
b := 'This' , ' ', 'String'.
a == b "false".
a = b "true"
f := 2 / 3.
g := 2 / 3.
f = g "true".
f == g "false"
Generally speaking SmallInteger
s which are =
are also ==
because the VM encodes them in a special way.
n := 3 + 4.
m := 2 + 5.
n = m "true".
n == m "true".
Another interesting case happens with instances of the class Symbol
s := #symbol.
t := 'symbol' asSymbol.
s = t "true".
s == t "true!"
This happens because the Symbol
class makes sure that no two instances with the same underlying string will ever exist in the environment.
=
means "is equal to" or "has the same value as". ==
means "is identical to" or "is the same object as". –
Zenger Yes, ==
is identity and it uses a primitive to compare if pointers point to the same address (i.e. same object).
=
is equality, meaning that two objects are equal, although they may be 2 different objects. By default =
uses ==
, but it can be reimplemented. Also when you reimplement =
it's advised to also reimplement hash
so hashed collections won't go crazy
==
I say: check if it is the same object –
Livable TL;DR: = : "equals", == : "same as"
In Object>>#=
= anObject
"Answer whether the receiver and the argument represent the same
object. If = is redefined in any subclass, consider also redefining the
message hash."
^self == anObject
In ProtoObject>>#==
== anObject "Primitive. Answer whether the receiver and the argument are the same object (have the same object pointer). Do not redefine the message == in any other class! Essential. No Lookup. Do not override in any subclass. See Object documentation whatIsAPrimitive."
<primitive: 110>
self primitiveFailed
So, you can override = but not ==.
FWIW ProtoObject subclass: #Object
An example of an #= override is
´= aBag "Two bags are equal if (a) they are the same 'kind' of thing. (b) they have the same size. (c) each element occurs the same number of times in both of them"
(aBag isKindOf: Bag) ifFalse: [^false].
self size = aBag size ifFalse: [^false].
contents associationsDo: [:assoc|
(aBag occurrencesOf: assoc key) = assoc value
ifFalse: [^false]].
^true`
Which, compared to Dictionary
is a quite different beast (implementation of Bas in "interesting").
`Dictionary>>#= aDictionary "Two dictionaries are equal if (a) they are the same 'kind' of thing. (b) they have the same set of keys. (c) for each (common) key, they have the same value. See issue 16760 before changing"
self == aDictionary ifTrue: [^true].
self species == aDictionary species ifFalse: [^false].
self size = aDictionary size ifFalse: [^false].
self associationsDo: [:assoc|
(aDictionary at: assoc key ifAbsent: [^false]) = assoc value
ifFalse: [^false]].
^true`
Interestingly they share the same hash
method.
`Collection>>#hash "Answer an integer hash value for the receiver such that, -- the hash value of an unchanged object is constant over time, and -- two equal objects have equal hash values"
| hash |
hash := self species hash.
self size <= 10 ifTrue:
[self do: [:elem | hash := hash bitXor: elem hash]].
^hash bitXor: self size hash
` There is some smell with the self size <= 10, have fun tracing these calls.
For more basic objects, see these:
Have fun exploring.
In Spotter, hash #im
or = #im
gives you the implementers list, so you can quickly find out about them.
xor
-based hashes are generally suboptimal because they do not use bits of orders higher than the ones in the elements being xor
ed. –
Orlene © 2022 - 2024 — McMap. All rights reserved.
=
refers to equality (objects are equal in value) and==
refers to identity (objects are identical). See Equality versus Identity. – Zenger