ANSI, =, hash, Integer, Float
Stephan Rudlof
sr at evolgo.de
Thu Dec 19 13:45:25 UTC 2002
First a sorry for making so many mistakes...
Richard A. O'Keefe wrote:
> Stephan Rudlof <sr at evolgo.de> asked:
> What about
> - defining #= true and #hash equal
> applied to numerical equal rational numbers
> (SmallInteger, LargePositiveInteger, LargeNegativeInteger, Fraction)
> and
> - defining #= as undefined (raising an Exception)
> for comparisons of all these rationals with Floats
> (also if numerical equal)?
>
> If I understand this proposal, it is
> - = should do what it does now when the receiver and argument are
> both rational numbers or are both floating point numbers;
> - hash should do what it does now for rational numbers and floating point
> numbers;
> - <rational> = <float> and <float> = <rational>
> should both raise an exception whether the receiver and argument
> are numerically eaual or not.
Yes.
>
> That is certainly a self-consistent proposal.
> Never mind the details of ANSI, it clearly violates the INTENT of
> ANSI Smalltalk.
>
> *BUT* it creates a library that is significantly harder to understand.
> In general,
> if x < y is defined,
> then x <= y, x > y, x >= y, x = y, and x ~= y are also defined
> and their values are mutually consistent.
> In particular, we expect
> x < y iff y > x
> x <= y iff y >= x
> x = y iff (x = y) not
> x <= y iff x < y or: [x = y]
> x = y iff (x < y or: [y < x]) not {provided RHS is defined}
> (Hope I got those right.)
> Any definition of comparison operations which violates these laws is
> a definition which will seduce programmers into making mistakes, because
> the symbols won't _really_ mean what they _look_ as though they mean.
>
> Ada consistently forbids _all_ mixed mode comparisons and no confusion
> arises. Pascal consistently allows all mixed mode comparisons and no
> confusion arises. Allowing _some_ mixed mode comparisons while
> forbidding others (which could clearly be derived from the allowed ones)
> does not provide a design that is to grasp or use correctly.
>
> In short, I suggest that 1.0 = 1 should be forbidden ONLY if
> 1.0 <= 1 (and all the others) are _also_ forbidden.
Agreed.
>
> This would avoid the trap of making loops with bad end
> conditions like e.g.
>
> | anInteger |
> anInteger := 0.
> [self doSomething. anInteger := anInteger + 0.1]
> whileTrue: [anInteger <= 1.0]
>
> I don't understand what this example is supposed to demonstrate.
You have caught me here:
> Calling something anInteger when its value is almost always a Float
> is certainly a bad idea.
Definitely true.
> Writing a block whose value is always a Float in a context
> where a block that always returns a Boolean is required
> is also a bad idea.
It's plain wrong (it had been better to test the code).
The intent has been (now tested):
| aNum |
aNum := 0.
[aNum <= 1]
whileTrue:
[Transcript cr; show: aNum printString.
aNum := aNum + 0.1]
>
> I fail to see how an example that uses <= tells us anything about
> what we should do to ensure that #hash and #= are mutually consistent.
True for both old and corrected example.
> I especially fail to see how an example which always compares a
> Float with another Float tells us anything about mixed mode comparison.
I have to agree.
But the corrected example now shows mixed mode arithmetics, which should be
avoided IMO.
>
> What's more, undefine <= and people will still be able to write
> 0 to: 1.0 by: 0.1 do: [:anInteger | self doSomething]
>
> Would such a change break something?
>
> Yes.
>
> I am extremely puzzled here.
>
> I pointed out a problem in Squeak's #hash implementations,
> and people suddenly pounce like starving wolves on a lamb
> on #= as if there was a problem with #=.
Apparently I've been confused here.
Sorry for generating confusion for others and making you extra work.
>
>>From a _user's_ perspective, The Simplest Thing That Could Possibly Work
> is simply to fix #hash. I not only know that it can be done, I know how
> to do it. With the proviso that the code is for specification purposes
> only (I do NOT propose actually allocating a fresh Float each time):
>
> LargePositiveInteger>>hash ^self asFloat hash
> LargeNegativeInteger>>hash ^self asFloat hash
> Fraction>>hash ^self asFloat hash
> Float hash
> ^((self between: SmallIntger minVal and: SmallInteger maxval)
> and: self fractionPart = 0.0)
> ifTrue: [self truncated hash]
> ifFalse: ["the current basicAt: bitAnd: ... stuff goes here"]
>
> This ensures consistency by
> - forcing Float to use SmallInteger hash whenever the float
> could be equal to a SmallInteger
> - forcing rationals other than SmallInteger to use Float hash
>
> The change would be hidden entirely inside the Number classes.
>
>
>
May be reasonable or not (currently no opinion here).
But independently of your suggestion I don't like mixed mode arithmetics and
comparisons between rationals (Integers and Fractions) and Floats and would
like to forbid it.
Greetings,
Stephan
--
Stephan Rudlof (sr at evolgo.de)
"Genius doesn't work on an assembly line basis.
You can't simply say, 'Today I will be brilliant.'"
-- Kirk, "The Ultimate Computer", stardate 4731.3
More information about the Squeak-dev
mailing list
|