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