Leando wrote:
IMO, the method SmallInteger>>\ is not working properly with negative arguments and receivers. For instance, when evaluating -1 \ -2 one obtains -3 instead of -1 (or 1). Note that this behavior contradicts both the mathematical definition and the current implementation of Number>>\.
Yes; this was one of the numeric primitive bugs in the VM that I noted awhile back, but it looks like the fix for it somehow slipped through the cracks. Here's the changeset for the fix again. Note that the VM will have to be recompiled to fix this.
-- tim
Attachment converted: Cog:Numeric Primitive Fixes.cs (TEXT/R*ch) (0000B200)
OK Squeak-easies,
Here's the changes to make Squeak use double dispatching for it's math stuff. The first two files, MathDD1.cs and MathDD2.cs change the 4 basic math ops for the three basic Number types. File them in in order. The first adds the DD methods, the second changes the math selectors to use them.
The other three files do the same for Point. They add the DD for Point and integrate it with the changes made in the Math* files. They also do their best to make Point as protocol possible with Number as reasonable. At the same time, I tried to hunt down any messages that made Point act as if it were at the "top" of any sort of numerical coercion hierarchy.
Finally, I have a question. In doing this, I found that the by the time I was done with Point, it would have been much easier to make Point a subclass of Number. I could probably get rid of quite a few messages that way as well. Is there a good reason not to make Point a subclass of Number? If one were to create a filein that made these changes, how would one do it? That is, for the most part, the filein would redefine Point (with Number as its super) and then get rid of a whole bunch of methods. Can the change sorter be used to put together a filein that instead of adding/changing methods, actually removes them, or do I have to put that together myself?
Travis Griggs
BTW: These changes have been submitted to UIUC
'From Squeak 1.3 of Jan 16, 1998 on 28 January 1998 at 12:15:46 am'!
!Number methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:51'! zeroDivideError ^self error: 'Cannot divide by zero'! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:56'! differenceFromFloat: aFloat ^self primitiveFailed! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:56'! differenceFromFraction: aFraction ^aFraction asFloat - self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:56'! differenceFromInteger: anInteger ^anInteger asFloat - self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:06'! productFromFloat: aFloat ^self primitiveFailed! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:06'! productFromFraction: aFraction ^aFraction asFloat * self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:06'! productFromInteger: anInteger ^anInteger asFloat * self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:36'! quotientFromFloat: aFloat ^self = 0.0 ifTrue: [self zeroDivideError] ifFalse: [self primitiveFailed]! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:48'! quotientFromFraction: aFraction ^aFraction asFloat / self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/28/98 09:14'! quotientFromInteger: anInteger ^anInteger asFloat / self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:07'! sumFromFloat: aFloat ^self primitiveFailed! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:07'! sumFromFraction: aFraction ^aFraction asFloat + self! !
!Float methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:07'! sumFromInteger: anInteger ^anInteger asFloat + self! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:43'! differenceFromFloat: aFloat ^aFloat - self asFloat! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:10'! differenceFromFraction: aFraction ^aFraction numerator * denominator - (numerator * aFraction denominator) / (aFraction denominator * denominator)! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:10'! differenceFromInteger: anInteger ^anInteger * denominator - numerator / denominator! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:49'! productFromFloat: aFloat ^aFloat * self asFloat! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:12'! productFromFraction: aFraction ^aFraction numerator * numerator / (aFraction denominator * denominator)! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 09:12'! productFromInteger: anInteger ^anInteger * numerator / denominator! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:36'! quotientFromFloat: aFloat ^aFloat / self asFloat! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:38'! quotientFromFraction: aFraction ^aFraction numerator * denominator / (aFraction denominator * numerator)! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:37'! quotientFromInteger: anInteger ^anInteger * denominator / numerator! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:54'! sumFromFloat: aFloat ^aFloat + self asFloat! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:44'! sumFromFraction: aFraction ^aFraction numerator * denominator + (numerator * aFraction denominator) / (aFraction denominator * denominator)! !
!Fraction methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:54'! sumFromInteger: anInteger ^self primitiveFailed! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:43'! differenceFromFloat: aFloat ^aFloat - self asFloat! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:52'! differenceFromFraction: aFraction ^aFraction numerator - (self * aFraction denominator) / aFraction denominator! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/28/98 08:33'! differenceFromInteger: anInteger ^self negative == anInteger negative ifTrue: [self digitSubtract: anInteger] ifFalse: [(self digitAdd: anInteger) normalize]! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:49'! productFromFloat: aFloat ^aFloat * self asFloat! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:49'! productFromFraction: aFraction ^aFraction numerator * self / aFraction denominator! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/28/98 08:34'! productFromInteger: anInteger ^self digitMultiply: anInteger neg: self negative ~~ anInteger negative! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:36'! quotientFromFloat: aFloat ^aFloat / self asFloat! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/27/98 23:38'! quotientFromFraction: aFraction ^aFraction numerator / (aFraction denominator * self)! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/28/98 09:12'! quotientFromInteger: anInteger | quoRem | ^self = 0 ifTrue: [self zeroDivideError] ifFalse: [ quoRem _ self digitDiv: anInteger neg: self negative ~~ anInteger negative. (quoRem at: 2) = 0 ifTrue: [(quoRem at: 1) normalize] ifFalse: [(Fraction numerator: anInteger denominator: self) reduced]]! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:54'! sumFromFloat: aFloat ^aFloat + self asFloat! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/23/98 07:54'! sumFromFraction: aFraction ^aFraction numerator + (self * aFraction numerator) / aFraction denominator! !
!Integer methodsFor: 'arithmetic-DD' stamp: 'TAG 1/28/98 08:32'! sumFromInteger: anInteger ^self negative == anInteger negative ifTrue: [(self digitAdd: anInteger) normalize] ifFalse: [self digitSubtract: anInteger]! !
'From Squeak 1.3 of Jan 16, 1998 on 30 January 1998 at 12:53:38 am'!
!Float methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:57'! * aNumber "Primitive. Answer the result of multiplying the receiver by aNumber. Fail if the argument is not a Float. Essential. See Object documentation whatIsAPrimitive."
<primitive: 49> "get the argument to actually carry out the operation since it can tune the operation based on my type, if it's the same class then the primitive failure is raised there" ^aNumber productFromFloat: self! !
!Float methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:57'! + aNumber "Primitive. Answer the sum of the receiver and aNumber. Essential. Fail if the argument is not a Float. See Object documentation whatIsAPrimitive."
<primitive: 41> "get the argument to actually carry out the operation since it can tune the operation based on my type, if it's the same class then the primitive failure is raised there" ^aNumber sumFromFloat: self! !
!Float methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:57'! - aNumber "Primitive. Answer the difference between the receiver and aNumber. Fail if the argument is not a Float. Essential. See Object documentation whatIsAPrimitive."
<primitive: 42> "get the argument to actually carry out the operation since it can tune the operation based on my type, if it's the same class then the primitive failure is raised there" ^aNumber differenceFromFloat: self! !
!Float methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:58'! / aNumber "Primitive. Answer the result of dividing receiver by aNumber. Fail if the argument is not a Float. Essential. See Object documentation whatIsAPrimitive."
<primitive: 50> "get the argument to actually carry out the operation since it can tune the operation based on my type, if it's the same class then the primitive failure is raised there, as well as zero divide detection" ^aNumber quotientFromFloat: self! !
!Fraction methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:40'! * aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber productFromFraction: self! !
!Fraction methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:41'! + aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber sumFromFraction: self! !
!Fraction methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:46'! - aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber differenceFromFraction: self! !
!Fraction methodsFor: 'arithmetic' stamp: 'TAG 1/27/98 23:46'! / aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber quotientFromFraction: self! !
!Integer methodsFor: 'arithmetic' stamp: 'TAG 1/28/98 09:16'! * aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber productFromInteger: self! !
!Integer methodsFor: 'arithmetic' stamp: 'TAG 1/28/98 0-10:'! + aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber sumFromInteger: self! !
!Integer methodsFor: 'arithmetic' stamp: 'TAG 1/28/98 09:17'! - aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber differenceFromInteger: self! !
!Integer methodsFor: 'arithmetic' stamp: 'TAG 1/28/98 09:15'! / aNumber "get the argument to actually carry out the operation since it can tune the operation based on my type" ^aNumber quotientFromInteger: self! !
'From Squeak 1.3 of Jan 16, 1998 on 29 January 1998 at 12:25:34 am'!
!Number methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! differenceFromPoint: aPoint ^aPoint x - self @ (aPoint y - self)! !
!Number methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! productFromPoint: aPoint ^aPoint x * self @ (aPoint y * self)! !
!Number methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! quotientFromPoint: aPoint ^aPoint x / self @ (aPoint y / self)! !
!Number methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! sumFromPoint: aPoint ^aPoint x + self @ (aPoint y + self)! !
!Point methodsFor: 'arithmetic' stamp: 'TAG 1/29/98 0-24:'! negated "Answer a point whose x and y coordinates are the negatives of those of the receiver. 6/6/96 sw"
^ x negated @ y negated! !
!Point methodsFor: 'arithmetic' stamp: 'TAG 1/29/98 0-24:'! reciprocal ^x reciprocal @ y reciprocal! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! exp ^x exp @ y exp! ]style[(19)f1b! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! floorLog: aNumber ^(x floorLog: aNumber) @ (y floorLog: aNumber)! ]style[(65)f1b! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! ln ^x ln @ y ln! ]style[(16)f1b! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! log: aNumber ^(x log: aNumber) @ (y log: aNumber)! ]style[(50)f1b! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! raisedTo: aNumber "Number compatibility" ^(x raisedTo: aNumber) @ (y raisedTo: aNumber)! ]style[(41 48)f1b,f1! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! raisedToInteger: aNumber "Number compatibility" ^(x raisedToInteger: aNumber) @ (y raisedToInteger: aNumber)! ]style[(48 6 16 15 16 9)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! sqrt ^x sqrt @ y sqrt! ]style[(22)f1b! !
!Point methodsFor: 'mathematical functions' stamp: 'TAG 1/29/98 0-24:'! squared ^x squared @ y squared! ]style[(31)f1b! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/29/98 0-24:'! ceiling "return the point where both x and y are the ceiling values of the receiver" ^x ceiling @ y ceiling! ]style[(109)f1b! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/29/98 0-24:'! floor "return the point where both x and y are the ceiling values of the receiver" ^x floor @ y floor! ]style[(103)f1b! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/29/98 0-24:'! roundTo: aNumber "implemented like Number of upwards scalability"
^(self / aNumber) rounded * aNumber! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/29/98 0-24:'! truncated "Answer a Point that is the receiver's x and y truncated by removing the fractional part."
^x truncated @ y truncated! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! differenceFromFloat: aFloat ^aFloat - x @ (aFloat - y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! differenceFromFraction: aFraction ^aFraction - x @ (aFraction - y)! ]style[(33 3 9 8 9 5)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! differenceFromInteger: anInteger ^anInteger - x @ (anInteger - y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! differenceFromPoint: aPoint ^aPoint x - x @ (aPoint y - y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! productFromFloat: aFloat ^aFloat * x @ (aFloat * y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! productFromFraction: aFraction ^aFraction * x @ (aFraction * y)! ]style[(30 3 9 8 9 5)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! productFromInteger: anInteger ^anInteger * x @ (anInteger * y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! productFromPoint: aPoint ^aPoint x * x @ (aPoint y * y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! quotientFromFloat: aFloat ^aFloat / x @ (aFloat / y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! quotientFromFraction: aFraction ^aFraction / x @ (aFraction / y)! ]style[(31 3 9 8 9 5)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! quotientFromInteger: anInteger ^anInteger / x @ (anInteger / y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! quotientFromPoint: aPoint ^aPoint x / x @ (aPoint y / y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! sumFromFloat: aFloat ^aFloat + x @ (aFloat + y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! sumFromFraction: aFraction ^aFraction + x @ (aFraction + y)! ]style[(26 3 9 8 9 5)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! sumFromInteger: anInteger ^anInteger + x @ (anInteger + y)! !
!Point methodsFor: 'arithmetic-DD' stamp: 'TAG 1/29/98 0-23:'! sumFromPoint: aPoint ^aPoint x + x @ (aPoint y + y)! !
'From Squeak 1.3 of Jan 16, 1998 on 29 January 1998 at 12:22:38 am'!
!Point methodsFor: 'arithmetic' stamp: 'TAG 1/29/98 0-24:'! // scale "reimplemented for upwards scalability"
^(self / scale) floor! !
!Point methodsFor: 'arithmetic' stamp: 'TAG 1/29/98 0-24:'! quo: scale "implemented as such for upwards scalability"
^(self / scale) truncated! !
'From Squeak 1.3 of Jan 16, 1998 on 30 January 1998 at 12:56:18 am'!
!Point methodsFor: 'arithmetic' stamp: 'TAG 1/29/98 0-24:'! rem: aNumericalThing "implemented just like Number, for upwards scalability"
^self - ((self quo: aNumericalThing) * aNumericalThing)! ]style[(11 89 6 13 6 10)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'arithmetic' stamp: 'TAG 1/29/98 0-24:'! \ aNumericalThing "implemented just like Number, for upwards scalability"
^self - (self // aNumericalThing * aNumericalThing)! ]style[(18 77 15 3 15 1)f1b,f1,f1b,f1,f1b,f1! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/29/98 0-24:'! roundTo: aNumericalThing "implemented like Number for compatibility"
^(self / aNumericalThing) rounded * aNumericalThing! ]style[(24 85 15)f1b,f1,f1b! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/30/98 0-24:'! roundUpTo: aNumber "same as Number, but for points"
^(self/aNumber) ceiling * aNumber! !
!Point methodsFor: 'truncation and round off' stamp: 'TAG 1/29/98 0-24:'! truncateTo: aNumericalThing "implemented just like Number"
^(self quo: aNumericalThing) * aNumericalThing! ]style[(27 48 15 4 15)f1b,f1,f1b,f1,f1b! !
At 22:45 -0500 1/31/98, Travis or Kerrin Griggs wrote:
In doing this, I found that the by the time I was done with Point, it would have been much easier to make Point a subclass of Number. I could probably get rid of quite a few messages that way as well. Is there a good reason not to make Point a subclass of Number?
Yes, points are not magnitudes! The definition of #< in Point contains the note:
"Answer whether the receiver is above and to the left of aPoint."
while #> contains the comment:
"Answer whether the receiver is below and to the right of aPoint."
Consider the following examples. In the first case, (2@2) is indeed less than (3@3). In the next two however, (4@6) is neither greater than nor less than (5@5).
(2@2) < (3@3) true
(5@5) < (4@6) false
(5@5) > (4@6) false
However, to be a magnitude #< and #> must be defined for all values such that if the values are not equal either #< or #> will answer true. The comment for class Magnitude says:
I am an abstract representation of objects that measure something linear.
If one object can be neither less-than nor greater-than another then the objects are not 'linear'.
Note that at least one major Smalltalk implementation (not IBM) gets this wrong. I guess it's OK if inheritence is considered strictly for implementation purposes, but that's how one ends up with Process being a subclass of a collection (as in some Digitalk systems).
I'd vote for having some is-a view point mixed in with the only-implementation viewpoint.
Dave
_______________________________ David N. Smith IBM T J Watson Research Center Hawthorne, NY _______________________________ Any opinions or recommendations herein are those of the author and not of his employer.
David N. Smith wrote:
At 22:45 -0500 1/31/98, Travis or Kerrin Griggs wrote:
In doing this, I found that the by the time I was done with Point, it would have been much easier to make Point a subclass of Number. I could probably get rid of quite a few messages that way as well. Is there a good reason not to make Point a subclass of Number?
Yes, points are not magnitudes! The definition of #< in Point contains the note:
"Answer whether the receiver is above and to the left of aPoint."
while #> contains the comment:
"Answer whether the receiver is below and to the right of aPoint."
Consider the following examples. In the first case, (2@2) is indeed less than (3@3). In the next two however, (4@6) is neither greater than nor less than (5@5).
(2@2) < (3@3) true
(5@5) < (4@6) false
(5@5) > (4@6) false
However, to be a magnitude #< and #> must be defined for all values such that if the values are not equal either #< or #> will answer true. The comment for class Magnitude says:
I am an abstract representation of objects that measure something linear.
If one object can be neither less-than nor greater-than another then the objects are not 'linear'.
Note that at least one major Smalltalk implementation (not IBM) gets this wrong. I guess it's OK if inheritence is considered strictly for implementation purposes, but that's how one ends up with Process being a subclass of a collection (as in some Digitalk systems).
I'd vote for having some is-a view point mixed in with the only-implementation viewpoint.
Good points. I have never found any use for the Point < method as defined. I have had reason to sort Points quite often, and have always done so using the following sort block:
[:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]
True, it establishes a convention, but I've never wanted ambiguous sorting of Points. Why not change the definition of < for Point. Does anyone use it to their benefit as implemented now, and in such a way that would make the above undesireable?
What are the arguments against turning the heirarchy there upside down? IOW
Object ArithmeticValue LinearMagnitude Number Point
Travis Griggs Key Technology tgriggs@keyww.com
Is there a good reason not to make Point a subclass of Number?
Yes, points are not magnitudes! The definition of #< in Point contains the note: "Answer whether the receiver is above and to the left of aPoint." while #> contains the comment: "Answer whether the receiver is below and to the right of aPoint."
... I have had reason to sort Points quite often, and have always done so using the following sort block:
[:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]
True, it establishes a convention, but I've never wanted ambiguous sorting of Points. Why not change the definition of < for Point.
(1) What are some of the reasons you (Travis) or anyone has had to sort Points linearly?
(2) Why not implement the range comparison messages to #shouldNotImplement or something like that?
(3) Point is a two-dimensional value. It cannot be made linear. Maybe the Block above works for a specific purpose. But it is no better than the relational comparison definitions, above. I'd vote for (#2), throwing an exception, as the simplest solution that makes the most general sense. YMMV.
What are the arguments against turning the heirarchy there upside down? IOW
Object ArithmeticValue LinearMagnitude Number Point
Some Magnitudes should not implement the math messages.
Patrick Logan wrote:
Is there a good reason not to make Point a subclass of Number?
Yes, points are not magnitudes! The definition of #< in Point contains the note: "Answer whether the receiver is above and to the left of aPoint." while #> contains the comment: "Answer whether the receiver is below and to the right of aPoint."
... I have had reason to sort Points quite often, and have always done so using the following sort block:
[:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]
True, it establishes a convention, but I've never wanted ambiguous sorting of Points. Why not change the definition of < for Point.
(1) What are some of the reasons you (Travis) or anyone has had to sort Points linearly?
I did a bunch of stuff where things were stored in grids (non-continuous) and I was always wanting to sort them in the same repeatable fashion.
(2) Why not implement the range comparison messages to #shouldNotImplement or something like that?
I like this. I like this alot. And with double dispatching, it would be cinche. Point might implement:
Point>>lessFromNumber: aNumber ^self shouldNotImplement
(3) Point is a two-dimensional value. It cannot be made linear. Maybe the Block above works for a specific purpose. But it is no better than the relational comparison definitions, above. I'd vote for (#2), throwing an exception, as the simplest solution that makes the most general sense. YMMV.
What are the arguments against turning the heirarchy there upside down? IOW
Object ArithmeticValue LinearMagnitude Number Point
Some Magnitudes should not implement the math messages.
Woops, duh. I should turm my brain on and think before I suggest these things. They say that the only dumb question is one that is not asked... :)
Travis Griggs Key Technology tgriggs@keyww.com
At 1:45 PM -0500 2/2/98, Patrick Logan wrote:
[snip]
(3) Point is a two-dimensional value. It cannot be made linear.
(I'm assuming that by 'linear' one means 'having a one-to-one correspondence with the natural numbers'.)
(Smalltalk) points are ordered pairs of integers (not of reals), correct? Thus, they have a mapping to the rationals.
1@1 --> 1/1 1@2 --> 1/2 1@3 --> 1/3 etc.
Since the rationals are denumerable, there is a mapping from them onto the naturals, and therefore there is a mapping from points onto the naturals, i.e., a linear ordering...
....though, admittedly, not the most practical ordering, but it's pretty easy to rearrange things so they come out more useful.r
(Given that points are most often, I would guess, used in finite rows, there is a very useful set of linear orderings, 1@1 < 1@2 ... < 1@m < 2@1 .... which is implemented by Travis' sort block, I believe.)
Or did I misunderstand your point (either the argumentative one, the Smalltalk one, or the mathematical one ;)?
[snip]
Cheers, Bijan Parsia
Bijan Parsia writes:
At 1:45 PM -0500 2/2/98, Patrick Logan wrote:
(3) Point is a two-dimensional value. It cannot be made linear.
(I'm assuming that by 'linear' one means 'having a one-to-one correspondence with the natural numbers'.)
(Smalltalk) points are ordered pairs of integers (not of reals), correct? Thus, they have a mapping to the rationals.
1@1 --> 1/1 1@2 --> 1/2 1@3 --> 1/3 etc.
I think the point is that Magnitude assumes that: (a > b) | (a < b) | (a = b)
But when you consider, say, the points 2@4 and 1@2, they are neither less than or more than each other -- but they are not equal either. (the same problem would occur in a mapping x@y --> x+y, and any other reasonable mappings I can think of from points onto numbers).
The ordering brought up before: a > b ==> (a x > b x) | ((a x = b x) & (a y > b y)) satisfies the requirement (even though it isn't a map onto numbers). However, it's rather arbitrary -- useful for, say, sorting, but not much else. It's consistent but not meaningful. The current implementation is (sort of) meaningful but not consistent.
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- || Ian Bicking | bickiia@earlham.edu || || drawer #419 Earlham College | (765) 973-2537 || || Richmond, IN 47374 | || -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Bijan Parsia wrote:
At 1:45 PM -0500 2/2/98, Patrick Logan wrote:
[snip]
(3) Point is a two-dimensional value. It cannot be made linear.
(I'm assuming that by 'linear' one means 'having a one-to-one correspondence with the natural numbers'.)
(Smalltalk) points are ordered pairs of integers (not of reals), correct?
Nope. x and y can be arbitrary objects, although in practice mostly numbers are used. Not necessarily integer, though.
Hans-Martin
At 13:37 -0500 2/2/98, Travis Griggs wrote:
Good points. I have never found any use for the Point < method as defined. I have had reason to sort Points quite often, and have always done so using the following sort block:
[:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]
True, it establishes a convention, but I've never wanted ambiguous sorting of Points. Why not change the definition of < for Point. Does anyone use it to their benefit as implemented now, and in such a way that would make the above undesireable?
Some random thoughts (sorted):
(1) Well, an inside rectangle test is something like:
(p1 >= p origin) & (p1 <= p corner)
(which is the definition of #between:and: in IBM Smalltalk, but not in Squeak).
(2) Making such a change would be incompatible with other Smalltalk systems (and the new almost-standard if I recall correctly).
(3) One thing bothers me about your sort block is that it presumes that points are lineralized along the Y axis. That is, a < b if its y value is less. Only if they are equal are the x values considered.
| lessThan | lessThan := [:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]. lessThan value: 6@3 value: 4@5 true
This result just looks odd to me: a value with a higher x coordinate being less than one with a lower coordinate.
I guess the first test could use x coordinates, and I'd feel better, but why one over the other?
(4) How about testing some other value, like the magnitude of the point (thinking in polar coordinates for a moment)? This isn't too slow if one compares the squares of the coordinates and fakes the signs.
(5) Points are holders for numbers. One can well have a point with floating point or fraction values, or a mixture. This is quite valid:
(2/3)@7.5
Some contexts may assume that points have integer coordinates, but that's their problem. If you add Point to some number hierarchy, what kind of coordinates classes are allowed? This is valid now:
((2/3)@7.5) * (2/3)
Produces: (4/9)@5.0
Would it still be? Should it be valid now?
Dave
_______________________________ David N. Smith IBM T J Watson Research Center Hawthorne, NY _______________________________ Any opinions or recommendations herein are those of the author and not of his employer.
David N. Smith wrote:
At 13:37 -0500 2/2/98, Travis Griggs wrote:
Good points. I have never found any use for the Point < method as defined. I have had reason to sort Points quite often, and have always done so using the following sort block:
[:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]
True, it establishes a convention, but I've never wanted ambiguous sorting of Points. Why not change the definition of < for Point. Does anyone use it to their benefit as implemented now, and in such a way that would make the above undesireable?
Some random thoughts (sorted):
(1) Well, an inside rectangle test is something like:
(p1 >= p origin) & (p1 <= p corner)
(which is the definition of #between:and: in IBM Smalltalk, but not in Squeak).
(2) Making such a change would be incompatible with other Smalltalk systems (and the new almost-standard if I recall correctly).
(3) One thing bothers me about your sort block is that it presumes that points are lineralized along the Y axis. That is, a < b if its y value is less. Only if they are equal are the x values considered.
| lessThan | lessThan := [:a :b | a y = b y ifTrue: [a x < b x] ifFalse: [a y < b y]]. lessThan value: 6@3 value: 4@5 true
This result just looks odd to me: a value with a higher x coordinate being less than one with a lower coordinate.
I guess the first test could use x coordinates, and I'd feel better, but why one over the other?
(4) How about testing some other value, like the magnitude of the point (thinking in polar coordinates for a moment)? This isn't too slow if one compares the squares of the coordinates and fakes the signs.
(5) Points are holders for numbers. One can well have a point with floating point or fraction values, or a mixture. This is quite valid:
(2/3)@7.5
Some contexts may assume that points have integer coordinates, but that's their problem. If you add Point to some number hierarchy, what kind of coordinates classes are allowed? This is valid now:
((2/3)@7.5) * (2/3)
Produces: (4/9)@5.0
Would it still be? Should it be valid now?
I do things like the above all the time. I don't ever assume that Points are composed of two SmallInteger values. If I do, I send truncated, or rounded, or ceiling, or floor to them and get one that I know does abide by that rule. I find the ability to mix simple numbers with Points very powerful and expressive. I find it even more so with other numeric types I've done before. But I wanted to get Point solved before implementing those.
This discussion has spiraled somewhat (which is a good thing I guess). A couple of points stand out to me, not all conclusive. A large part of this discussion seems to revolve around "why to inherit." For implementation or interface. Other languages avoid (not necessarily solve) this problem by separating the two. My proposition that Point be made a subclass of Number was driven by the implementation one. The majority of Number's interface was also applicable to Point, and could be implemented using the exact same code. It was driven by the fact that as I made Point's interface be more compatible with Number's, I found myself copy/pasting most of the methods. From a classification view though, 'twould seem that Point should not be a subclass of Number - not because of what Number is, but because of what Magnitude is. Some MI pundits would argue this is why we should have MI - I don't know.
As for testing whether Point is less than another Point - it's all a hack. My suggestion being one of the most obvious. What an obvious kludge to get around an inheritence problem! And any method that gets put in place to make Point obey < will be a hack, designed to somehow plug up the hole that is caused by inheriting ultimately from Magnitude. So... what to do in a case like this? Inherit an interface that is maybe not quite correct (but close) and reduce the amount of duplicate code, or keep the inheritence tree pure and duplicate separate code trees?
It seems that from the way I understand what Self does (and I'm not too solid on this either), that delegation would solve this problem?
Travis Griggs Key Technology tgriggs@keyww.com
FWIW, I think you may be dangerously close to reinventing APL. :-)
On Mon, 2 Feb 1998, Travis Griggs wrote:
Point, and could be implemented using the exact same code. It was driven by the fact that as I made Point's interface be more compatible with Number's, I found myself copy/pasting most of the methods. From a classification view though, 'twould seem that Point should not be a subclass of Number - not because of what Number is, but because of what Magnitude is. Some MI pundits would argue this is why we should have MI - I don't know.
squeak-dev@lists.squeakfoundation.org