[comp.lang.smalltalk] PP Smalltalk R4 Query, Bug?

pnm@goanna.cs.rmit.oz.au (Paul Menon) (03/08/91)

hi,
  I am using Objectworks\Smalltalk Release 4 (what a mouthful) on a Mac
  and came up against an entry in the User's Guide which makes me worry..
  I wasn't getting very good behaviour with certain pieces of code I wrote, 
  so I went back to basics.

  Pages 30-31 (Messages in sequence) make mention of two ways of doing
  things, one more efficient ("reduces the wordiness of the code, though often
  at the expense of readability" - that's not all!).

    One way:
	| aDictionary anAssociation |
	aDictionary := Dictionary new.
	anAssociation := #Three->3.
	aDictionary add: anAssociation.

    The other (more efficient?) way:
	| aDictionary |
	aDictionary := Dictionary new add: #Three->3.

    
    The second way definitely reduces verbiage, but produces
    an Association, not a Dictionary!!!

    I've looked up the appendix on implementation limits and there are some
    cases which we are warned of, but not this.  Even if it were there, why
    present it in the User Guide as an example?

    On experimenting, I noticed a way around:
	| aDictionary |
	(aDictionary := Dictionary new) add:#Three->3.

    This produces the right result.  I determined this by debugging and
    examining the compiled code.  The 'source' and 'destination' are shown as
    a summary below.
	
	| aDict bDict cDict dDict anAssociation |
	anAssociation := #Three ->3.
	aDict := Dictionary new add: anAssociation.		[1]
	bDict  := Dictionary new.				[2]
	bDict add: anAssociation.				[2]
	(cDict := Dictionary new) add: (#Three->3).		[3]
	dDict := (Dictionary new) add:#Three->3.		[4]


	The resulting compiled code is below (note the way in which the two
	bDict statements are handled - ie, correctly, but compressed into
	one line).  Note also how it treats that and the previous statement
	[1] in two different ways (even with t5 explicitly as #Three->3, ie
	I tried it both ways).

	| t1 t2 t3 t4 t5 |
	t5 := #Three -> 3.
	t1 := Dictionary new add: t5.
	(t2 := Dictionary new) add: t5.
	(t3 := Dictionary new) add: #Three -> 3.
	t4 := Dictionary new add: #Three -> 3.

    Of the above, only t2 (bDict, or statement(s) [2]) and t3 (cDict, or [3]) 
    produce the required Dictionary, the others are Associations.

    What's the score? Is this..
    *	A bug?
    *	A feature?
    *	Something which disappears when not in 'interactive' mode?
    *	A (:=, new, add:) binding which was not anticipated?
    *	My imagination?

If anyone can replicate this, my brain would appreciate it %:->
I'd also appreciate any feedback from ParcPlace.

(apologies if this has already been discussed)

Thanks,

    Paul Menon,
    Dept of Computer Science,
    Royal Melbourne Institute of Technology, 
    124 Latrobe Street,
    Melbourne 3001, 
    Victoria, Australia.

pnm@goanna.cs.rmit.oz.au
PH:	+61 3 660 3209

johnson@cs.uiuc.EDU (Ralph Johnson) (03/08/91)

Paul mention asks about the fact that
	| aDictionary anAssociation |
	aDictionary := Dictionary new.
	anAssociation := #Three->3.
	aDictionary add: anAssociation.

is different from
	| aDictionary |
	aDictionary := Dictionary new add: #Three->3.

These expressions have always been different, because
the add: messages for collections always returns the
argument, not the receiver.  Lot of people think that
this is a design bug, but there *is* a reason for it.

The reason is because of cascaded messaged.
	aCollection add: thing1;
		    add: thing2;
		    add: thing3
will only work if add: returns the receiver.

Summary: the ParcPlace documentation was wrong, since
the example is supposed to work the way to describe it,
not the way the documentation said.

Ralph Johnson -- University of Illinois at Urbana-Champaign

huba@ls5.informatik.uni-dortmund.de (Hubert Baumeister) (03/08/91)

In article <1991Mar8.140425.10863@m.cs.uiuc.edu>, johnson@cs.uiuc.EDU (Ralph Johnson) writes:
...
|> These expressions have always been different, because
|> the add: messages for collections always returns the
|> argument, not the receiver.  Lot of people think that
|> this is a design bug, but there *is* a reason for it.
|> 
|> The reason is because of cascaded messaged.
|> 	aCollection add: thing1;
|> 		    add: thing2;
|> 		    add: thing3
|> will only work if add: returns the receiver.
|> 

Ralph, I do not understand  your example. These cascaded messages work 
independend of the result of the add: message. On the other hand if you used
the expression
	((aCollection add: thing1)
		    add: thing2)
		    add: thing3
then this would require that add: returns the receiver. To overcome the problem
that add: returns the argument, one can allways use yourself to get the 
collection back like in:
	aCollection := Collection new add: thing1;
		    add: thing2;
		    add: thing3; yourself.


|> Ralph Johnson -- University of Illinois at Urbana-Champaign

Hubert Baumeister
(huba@ls5.informatik.uni-dortmund.de)

pnm@goanna.cs.rmit.oz.au (Paul Menon) (03/09/91)

hi,
  Thankyou to all those who responded via mail & news.  All the email
  responders (huba@ls5.informatik.uni-dortmund.de, cca@physics.purdue.edu,
  sbb@Eng.Sun.COM and knight@mrco.carleton.ca so far) pointed out that

      aDict := Dictionary new add: #Three->3
		    and
      (aDict := Dictionary new) add: #Three->3
  
  return totally different types of object.

  Yes, I was aware of this feature in Smalltalk (ie, add: returning the 
  added object, not the object into which it was added), but I must've been
  under the influence of vertigo, occasionally suffered by those in a 
  multilingual programming environment.  To make matters worse, it appears
  that the manual writer(s) of the User's Guide fell for the same trap.  Oh
  well, I have company anyway.  And I'm glad it isn't a Smalltalk bug.

  The moral of the story?  RTFM doesn't always work!

  (I'm pretty sure there wasn't a "; yourself" appended to the first statement
  above in the User's Guide, but I'll check when I'm at work again).

  Once again, Thankyou for the swift response.

    Paul Menon,  (the room's still spinning...)
    Dept of Computer Science,
    Royal Melbourne Institute of Technology, 
    124 Latrobe Street,
    Melbourne 3001, 
    Victoria, Australia.

pnm@goanna.cs.rmit.oz.au
PH:	+61 3 660 3209

pnm@goanna.cs.rmit.oz.au (Paul Menon) (03/09/91)

In article <4932@goanna.cs.rmit.oz.au>, pnm@goanna.cs.rmit.oz.au (Paul Menon) writes:
> 
>       aDict := Dictionary new add: #Three->3
> 		    and
>       (aDict := Dictionary new) add: #Three->3
>   
>   return totally different types of object.

That is, return totally different types of object into aDict (groan..)

Paul

johnson@cs.uiuc.EDU (Ralph Johnson) (03/10/91)

Hubert Baumeister pointed out that my explanation of why the Smalltalk
class library inventers made add: return the argument was completely
wrong, so I'll make a second attempt.

There are two choice, either return the receiver or the argument.
The way things work now, one can either send a message to the the
reciever using cascaded messages or to the argument using parantheses.
The standard example of the first situation is

	aCollection add: thing1;
		    add: thing2;
		    add: thing3

An example of the second situation is

	(aCollection add: OrderedCollection new)
		add: thing1;
		add: thing2

which puts thing1 and thing2 in the OrderedCollection.
If add: returned its argument then the first example could
be recoded without cascaded messages, but the second example
would require a temporary variable.

I am pretty sure that this is the reason for the strange semantics
of add:.  Note that I am not arguing in its favor, I am just
explaining it.  I've had to explain it over and over again,
and I'm pretty tired of it.  In my opinion, a few temporary
variables would be better than a feature that almost everyone
gets wrong at first.  However, it is too late to change it now!

Ralph Johnson -- University of Illinois at Urbana-Champaign

rick@cua.cary.ibm.com (Rick DeNatale) (03/12/91)

In article <1991Mar8.140425.10863@m.cs.uiuc.edu> johnson@cs.uiuc.EDU 
(Ralph Johnson) writes:

>These expressions have always been different, because
>the add: messages for collections always returns the
>argument, not the receiver.  Lot of people think that
>this is a design bug, but there *is* a reason for it.
>
>The reason is because of cascaded messaged.
>	aCollection add: thing1;
>		    add: thing2;
>		    add: thing3
>will only work if add: returns the receiver.
>

I don't believe that cascading would work any differently if add: answered
the receiver or not.  (Actually it answers the argument, not the receiver).
Cascading sends the message to the receiver of the previous message regardless
of the answer of that message.

The real reason (at least in my mind) that add: answers the argument is 
because it follows the model of at:put:, which returns its argument so
you can write code like:

         value := aDictionary at: someKey
                     ifAbsent: [ aDictionary at: someKey put: someValue]

Rick DeNatale

khaw@parcplace.com (Mike Khaw) (03/12/91)

In <4931@goanna.cs.rmit.oz.au> pnm@goanna.cs.rmit.oz.au (Paul Menon) writes:


>    The other (more efficient?) way:
>	| aDictionary |
>	aDictionary := Dictionary new add: #Three->3.

>    
>    The second way definitely reduces verbiage, but produces
>    an Association, not a Dictionary!!!

>    I've looked up the appendix on implementation limits and there are some
>    cases which we are warned of, but not this.  Even if it were there, why
>    present it in the User Guide as an example?

>    On experimenting, I noticed a way around:
>	| aDictionary |
>	(aDictionary := Dictionary new) add:#Three->3.

It's an error in the User's Guide. In the first version, the result of
the add: message (an Association) is assigned to aDictionary, because
assignment has lower precedence than unary and selector messages. In
the second version, the assignement happens first, then its result (a
new instance of Dictionary, referenced as aDictionary) receives the
add: message. Although the result returned by the add: message is
still an instance of Association, nothing uses the return value.
--
Mike Khaw
ParcPlace Systems, Inc., 1550 Plymouth St., Mountain View, CA 94043
Domain=khaw@parcplace.com, UUCP=...!{uunet,sun,decwrl}!parcplace!khaw

white@scs.carleton.ca (Paul White) (03/13/91)

Just to add my 2 cents worth, a more probable explanation for the semantics
of the add: (and at:put:) message returning the arguement as opposed to 
the receiver is to keep it consistent with the remove: protocol.  

Consider this: since when you 'pop' an item from a stack you expect to get the  
popped item back, why shouldn't you get the 'pushed' item back when you
send a push: message.  The semantics should be the same for all collections,
be it stacks or dictionaries.

Paul White
white@scs.carleton.ca