The standard does *not* support - a removeAll: a - [was: Re: [BUG] Collection>>removeAll:]

Lex Spoon lex at cc.gatech.edu
Sat Sep 7 20:19:32 UTC 2002


"Richard A. O'Keefe" <ok at cs.otago.ac.nz> wrote:

> But that's not what he gives an example of.
> 
> 	For example, suppose you looked at the
> 	following code, which worked:
> 	
> 		a removeAll: a
> 	
> Right.  Here is a call to an operation *inside* an encapsulation
> boundary which can do whatever it wants.
> 
> 	and then changed it to this, which doesn't work:
> 	
> 		a do: [ :each | a remove: each ]
> 	
> I thought it was generally agreed in this and related discussions
> that good programmers _ought_ to know that it is a bad idea to modify
> a container while it is running an iterator method.

Note, that the programmer might not know that the two a's are the same:

	a do: [ :each | b remove: each ]

An error mesage lets the programmer know as soon as possible that a and
b are sometimes the same.


> 	It seems better to make it an error.
> 
> This argument has the form
>     "Because a working method call can be replaced by code
>      that is obviously unlikely to work,
>      therefore the working method should be made to signal an error."
> 
> I don't see any logic in that.  To start with, just how likely is it that
> someone would replace a call to a method in someone else's class with code
> that, even if it worked, might take O(N**2) time or worse?  

It is *not* obviously unlikely to work -- quite the contrary. 
Furthermore, the complexity will usually be the same.  In Squeak there
is but one implementation of removeAll:.

I make changes like this all the time, and I believe it is a common
Smalltalk development style.  Smalltalkers *adjust* code more than they
write it from scratch.  And they don't wait for proofs of identical
behavior before they make the adjustments; they try things that are
likely to work and then they run the program again.



> into
>     x := 0. [z = 0] whileFalse: [x := x+y. z := z-1]
> and if y or z was a floating-point number, the result could be grossly
> inaccurate.  Is that an argument for making * signal an error when given
> floating-point numbers, or returning equally inaccurate results?

A difference is that, with floating point, the problematic situation
can't be avoided.  With collections it can.

(In fact, floating point causes a lot of trouble!  I wish it was harder
to access, because it leads to needlessly inaccurate programs.)

> 
> In general, suppose there is some possibly fairly simple piece of code
> such that we as human beings can figure out what the author probably
> _meant_ to do, but it doesn't work.  Call that code B for broken.
> So we construct a new method which recognises the particular situation
> and produces a meaningful answer according to a clean semantics.  Call
> that code C for correct.  Now, the whole _point_ of developing C is so
> that C can replace B.
> 
> Lex Spoon's argument is that because someone could replace a call to C
> with the original B, C should signal an error.  That is, you should NEVER
> be able to replace broken code with working code.
> 
> I for one am unpersuaded.

There is more to my argument:

	1. It is an extremely special case.  Instances are likely to be
modified into instances of the general problem over time, and the
general problam cannot be solved.

	2. Relying on the extended semantics is unportable and should not be
encouraged.

	3. The situation is easily avoided once it's been found.


I guess what it comes down to is that I don't like do-what-I-meant. 
DWIM is always dangerous, and in this case we can avoid that danger
quite easily.


Lex



More information about the Squeak-dev mailing list