Custom equality test does not work for Set add: if ignoring instance variables

Hello,

a Set cannot add duplicate instances that are equal ( == )
even if the instances are not identical ( === ).
For example, one cannot add
equal instances of a string (“abc”) to a set more than once.
Furthermore, one can overwrite the == operator to make it
impossible to add duplicate instances whose contents are equal.
A working example exists in class NetAddr.
NetAddr overwrites the equal operator ==, and thus one cannot
add a “duplicate” NetAddr instance to a Set that already includes
another instance with the same address and port number.

However, in writing the == operator method for a my own
class, the following was observed:

When the operator compares all instance variables of the class for equality,
then the Set rejects equal instances, just as in NetAddr.
However, if another instance variable is added, then the Set
no longer rejects equal instances, even if the test of equality works
as expected.

Following are examples showing this.

Could anyone help out?
In the meanwhile, I am using a custom class that overrrides
“add” to work as expected.

Best,

Iannis Zannos

=======================

Examples:

  1. Set rejects second instances that equal (but not identical):
a = "alpha";
b = "alpha";
a == b; // a is equal to b
a === b; // a is not identical to b
c = Set();
c add: a; // a was added
c add: b; // b was rejected
c; // c only contains first instance
  1. Set rejects equal B1 instance
a = B1("alpha");
b = B1("alpha");
a == b; // a is equal to b
a === b; // a is not identical to b
c = Set();
c add: a; // a was added
c add: b; // b WAS REJECTED
c; // c ONLY CONTAINS FIRST INSTANCE
  1. Set DOES NOT REJECT equal B2 instance
a = B2("alpha");
b = B2("alpha");
a == b; // a is equal to b
a === b; // a is not identical to b
c = Set();
c add: a; // a was added
c add: b; // b WAS NOT REJECTED
c; // c CONTAINS BOTH INSTANCES

Class code for B1, B2 (see also attached file.)

B1 {
	var <path;

	*new { | path | ^this.newCopyArgs(path); }

	== { arg that; ^path == that.path }
}

B2 {
	var <path, <name;

	*new { | path | ^this.newCopyArgs(path); }

	== { arg that; ^path == that.path }
	// also tested with this, modeled after NetAddr:
	// == { arg that; ^this.compareObject(that, #[\path]) }
}

EqalityTests.sc (470 Bytes)

Away from computer so can’t check, but I think you need to add a hash.
Net addr does this…

hash {
   ^this.instVarHash(#[\port, \addr])
}

This is because the default hash of object is the identity hash.

Thanks for answering. Here is the corrected code for B2 that works:

B2 {
	var <path, <name;

	*new { | path | ^this.newCopyArgs(path); }

	// == { arg that; ^path == that.path }
	// also tested with this, modeled after NetAddr:
	== { arg that; ^this.compareObject(that, #[\path]) }

	hash {
		^this.instVarHash(#[\path])
	}
}

And here is the example for testing, it works this time:

a = B2("alpha");
b = B2("alpha");
a == b; // a is equal to b
a === b; // a is not identical to b
c = Set();
c add: a; // a was added
c add: b; // b WAS REJECTED
c; // c CONTAINS the first instance only