[comp.lang.smalltalk] A question...

preston@felix.UUCP (Preston Bannister) (08/04/87)

I have been writing a set of class definitions to support graphics in
Little Smalltalk from the descriptions in the Smalltalk-80 books.  

The class Point responds to '+'.  The examples show both:

	aPoint + aPoint		i.e.  (1 @ 2) + (3 @ 4) = (4 @ 6)

and

	aPoint + aNumber	i.e.  (1 @ 2) + 3 = (4 @ 5)

What I don't see is how to write the method for '+' without explicitly
testing the class of the parameter.  Something like:

  + delta
    (delta class == Point)
      ifTrue: [^ Point new x: (self x + (delta x)) y: (self y + (delta y)) ]
      ifFalse: [^ Point new x: (self x + delta) y: (self y + delta) ]

(This may not be correct Smalltalk.  I'm not that familiar with the
language and the book is not in front of me.)

Somehow I would have expected to write two methods:

  + aPoint
    ^ Point new x: (self x + (delta x)) y: (self y + (delta y)) 

  + aNumber
    ^ Point new x: (self x + delta) y: (self y + delta) 

But this doesn't seem possible.

Am I missing something??

========================================
Preston L. Bannister
USENET	   :	ucbvax!trwrb!felix!preston
BIX	   :	plb
CompuServe :	71350,3505
GEnie      :	p.bannister
--
========================================
Preston L. Bannister
USENET	   :	ucbvax!trwrb!felix!preston
BIX	   :	plb
CompuServe :	71350,3505
GEnie      :	p.bannister

stevev@tekchips.TEK.COM (Steve Vegdahl) (08/05/87)

In article <4357@felix.UUCP>, preston@felix.UUCP (Preston Bannister) writes:
> 
> The class Point responds to '+'.  The examples show both:
> 	aPoint + aPoint		i.e.  (1 @ 2) + (3 @ 4) = (4 @ 6)
> and
> 	aPoint + aNumber	i.e.  (1 @ 2) + 3 = (4 @ 5)
> What I don't see is how to write the method for '+' without explicitly
> testing the class of the parameter.  Something like:
> 
>   + delta
>     (delta class == Point)
>       ifTrue: [^ Point new x: (self x + (delta x)) y: (self y + (delta y)) ]
>       ifFalse: [^ Point new x: (self x + delta) y: (self y + delta) ]

I believe that most Smalltalk images have a coercion selector "asPoint",
where
	3@7 asPoint --> 3@7
but
	6 asPoint --> 6@6

Then your '+' method for Point looks something like:
	+ delta
	    | temp |
	  temp <- delta asPoint
	  ^(self x + temp x) @ (self y + temp y)

		Steve Vegdahl
		Computer Research Lab
		Tektronix Labs
		Beaverton, Oregon

patc@tekcrl.TEK.COM (Pat Caudill) (08/05/87)

>In article <4357@felix.UUCP>, preston@felix.UUCP (Preston Bannister) writes:
> 
> The class Point responds to '+'.  The examples show both:
> 	aPoint + aPoint		i.e.  (1 @ 2) + (3 @ 4) = (4 @ 6)
> and
> 	aPoint + aNumber	i.e.  (1 @ 2) + 3 = (4 @ 5)
> What I don't see is how to write the method for '+' without explicitly
> testing the class of the parameter.  Something like:

	Besides using the method that Steve Vegdahl describes, I
would like to suggest the method pointed out by Dan Ingalls in a
paper at the OOPSLA-86 conference. I will use the convention, as
he did, of showing the class of the receiver in <> before the message.

<Point> + aValue

    ^aValue pointPlus: self

Then define pointPlus in both number and point as

<Point> pointPlus: aPoint

    ^ (self x + aPoint x) @ (self x + aPoint y)

<Number> pointPlus: aPoint

    ^ (self + aPoint x) @ (self + aPoint y)

The full reference is:

Daniel H. H. Ingalls, "A Simple Technique for Handling Multiple
Polymorphism" OOPSLA'86 Conference Proceedings (Sigplan Notices
Vol 21 #1 (Nov 1986)) pp 347.

I hope this helps.
			Pat Caudill
			patc@tekcrl.TEK.COM

susser@parcvax.Xerox.COM (Josh Susser) (08/05/87)

In article <4357@felix.UUCP> preston@felix.UUCP (Preston Bannister) writes:
>
>I have been writing a set of class definitions to support graphics in
>Little Smalltalk from the descriptions in the Smalltalk-80 books.  
>
>The class Point responds to '+'.  The examples show both:
>	aPoint + aPoint		i.e.  (1 @ 2) + (3 @ 4) = (4 @ 6)
>and
>	aPoint + aNumber	i.e.  (1 @ 2) + 3 = (4 @ 5)
>
>What I don't see is how to write the method for '+' without explicitly
>testing the class of the parameter.  Something like:
>
>  + delta
>    (delta class == Point)
>      ifTrue: [^ Point new x: (self x + (delta x)) y: (self y + (delta y)) ]
>      ifFalse: [^ Point new x: (self x + delta) y: (self y + delta) ]
>
>Somehow I would have expected to write two methods:
>  + aPoint
>    ^ Point new x: (self x + (delta x)) y: (self y + (delta y)) 
>  + aNumber
>    ^ Point new x: (self x + delta) y: (self y + delta) 
>But this doesn't seem possible.
>
>Am I missing something??
>
>========================================
>Preston L. Bannister
>USENET	   :	ucbvax!trwrb!felix!preston
>BIX	   :	plb
>CompuServe :	71350,3505
>GEnie      :	p.bannister

Your instincts are right - explicitly testing the class of an object is
an evil thing to do. Besides, what if you want to add a subclass of Point?
Or something that just behaves like a Point?

There are two good ways to handle a situation like this. They both involve
writing more than one method.

The first way, you make sure that the argument is a Point, by coercing it if
necessary. Like so:

Integer:
	asPoint
		^self@self

Point:
	asPoint
		^self

	+ delta
		delta <- delta asPoint.
		^(x + delta x)@(y + delta y)


The other way is using a technique known as multiple polymorhism:

Integer:
	plusPoint: aPoint
		^(self + aPoint x)@(self + aPoint y)

Point:
	plusPoint: aPoint
		^(x + aPoint x)@(y + aPoint y)

	+ delta
		^delta plusPoint: self


Both of these techniques are nicer than explicitly testing the class of the
argument. The advantage here is that you can then add anything to a Point as
long as is understands "asPoint" or "plusPoint:". Who really cares what
class an object is as long as it behaves correctly anyway?


--Josh Susser
  susser.pasa@xerox.com
  susser@parcvax.xerox.com
  
When the going gets tough, it takes a brave man to lay back and party.

chips@usfvax2.UUCP (Chip Salzenberg) (08/06/87)

In article <4357@felix.UUCP>, preston@felix.UUCP writes:
> I have been writing a set of class definitions to support graphics in
> Little Smalltalk from the descriptions in the Smalltalk-80 books.  
> 
> The class Point responds to '+'.  The examples show both:
> 
> 	aPoint + aPoint		i.e.  (1 @ 2) + (3 @ 4) = (4 @ 6)
> and
> 	aPoint + aNumber	i.e.  (1 @ 2) + 3 = (4 @ 5)
> 
> What I don't see is how to write the method for '+' without explicitly
> testing the class of the parameter.
> 
> Preston L. Bannister
> USENET	   :	ucbvax!trwrb!felix!preston

Perhaps you could implement the method asPoint in the class Integer:

	asPoint
	"Return a Point with x and y both equal to self."
	^self @ self.

Then, in class Point:

	asPoint
	"Return self."
	^self.

and, then the method you wanted:

	+ delta
	"Return the sum of two points.  Delta may be an integer."
	| aPoint |
	aPoint := delta asPoint.
	^(self x) + (aPoint x) @ (self y) + (aPoint y).

This may not be very fast, but it would work.
-- 
Chip Salzenberg            UUCP: "uunet!ateng!chip"  or  "chips@usfvax2.UUCP"
A.T. Engineering, Tampa    Fidonet: 137/42    CIS: 73717,366
"Use the Source, Luke!"    My opinions do not necessarily agree with anything.

johnson@uiucdcsp.cs.uiuc.edu (08/06/87)

The + method for Point in Smalltalk-80 converts the argument to a point
and then adds it.  It looks something like

+ anArg
    | aPoint |
    aPoint <- anArg asPoint.
    ^(x + aPoint x) @ (y + aPoint y)

The asPoint message to a Point returns the receiver, while the asPoint
message to a number returns (self @ self).

This seems to me a kludge.  A better solution is to use "double dispatching"
to pick the method based on the classes of both the receiver and the
argument.  Dan Ingals had a paper describing this in OOPSLA'86.  The basic
idea is that the + method for Point would look something like

+ anArg
    ^anArg addToPoint: self

The class of anything that might ever be added to a point would then need
an addToPoint: method.  You can do this with all the number classes and
the arithmetic operators.  It results in a nicer system than the one in
Smalltalk-80 based on "coercion", especially if you want to have matrices,
polynomials, functions, etc. as part of your arithmetic.

I teach my students that good Smalltalk code should NEVER explicitly test
the class of an object.  Double dispatching is usually a better alternative.