简体   繁体   中英

What is the difference between = and == in Pharo Smalltalk?

What is the difference between = and == in Pharo Smalltalk? What are they named, one isEqual and the other?

= ~=    equality / inequality (deep)
== ~~   equality / inequality (shallow)

Yes, == is identity and it uses a primitive to compare if pointers point to the same address (ie 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

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

  1. Your implementation of = is robust (eg, banana = car gives no error) and
  2. Every time you redefine = 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 equal equal to b or a is identical to a 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.

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:

Magnitude >>#hash实现者

Have fun exploring.

In Spotter, hash #im or = #im gives you the implementers list, so you can quickly find out about them.

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM