[BUG]Collection>>removeAll:

Richard A. O'Keefe ok at cs.otago.ac.nz
Mon Aug 19 22:37:29 UTC 2002


"Chris Norton" <chrisn at Kronos.com> wrote:
	Richard O'Keefe's code is close, but it does not always return the
	collection of items that was passed in.

Yes, that's quite deliberate.  What it DOES do is always return SOME
collection that contains precisely the items that were removed.

	How about this:
	
	removeAll: aCollection 
	    "Remove each element of aCollection from the receiver.
	     If successful for each element, answer aCollection.
	     Otherwise create an error notification.
	     ArrayedCollections cannot respond to this message."

	    | itemsToRemove |
	    itemsToRemove := (aCollection == self)
		    ifTrue: [aCollection copy]
		    ifFalse: [aCollection].
	    itemsToRemove do: [:element | self remove: element].
	    ^aCollection

The question is, what is the important thing about the result of #removeAll:?

If it is the IDENTITY of aCollection, then Chris Norton's code (which differs
from mine only in returning aCollection) is the right way to go.

If it is the CONTENTS of aCollection, then my code (which differs from
Chris Norton's only in returning itemsToRemove) is the right way to go.

I have real trouble believing that the identity is all-important.
Suppose I do
    
    nItemsRemoved := (someCollection removeAll: otherCollection) size.

With my code, this will always work.  With Chris Norton's code, it will
return 0 when someCollection and otherCollection are the same object,
even if 100 000 000 items were removed, which seems to me MOST undesirable.

My understanding is the #removeAll: is supposed to return a collection
of all the items that were removed, and returning aCollection was just the
cheapest way to do this.

A check "find senders of removeAll:" showed me that nothing in Squeak 3.2
actually _cares_ what the result of #removeAll: is, so there are certainly
no grounds for supposing that it is the identity of the result rather than
its contents that matters.

As a matter of fact, there is something much much worse about #removeAll:
than this.

Suppose we do
    removed := someCollection removeAll: otherCollection.
and some element of otherCollection is NOT in someCollection.
Up pops "Error: Object is not in the collection."
Fine.
But what is the state of someCollection?
Practically speaking, we have to regard it as undefined.
Before we had exception handling, this didn't matter too much, but now
we do, and it is possible for a computation to recover from such a failed
operation and find itself holding onto objects in undefined states.
I have no quick-and-easy solution for that problem, in part because the
obvious check
    self includesAllOf: aCollection
doesn't work.
    a := #(1 2 3 4) asOrderedCollection.
    a removeAll: #(3 1 4 1)
would pass the "self includesAllOf: #(3 1 4 1)" test, but the
#removeAll: call fails because when the *second* 1 is removed,
it isn't there any more.




More information about the Squeak-dev mailing list