Why add: return an object added to the Smalltalk collection?

Background

Something that every Smalltalk newbie catches is that add: does not return "self", but the object being added.

For example, using this code:

 myCollection := OrderedCollection new add: 'Peter'; add: 'John'; add: 'Paul'. 

myCollection will contain the string "Gender", not the set itself.

This is because add: returns the object to be added, and the entire cascading expression is evaluated until the last message sent.

Instead, it should be written with yourself at the end:

 myCollection := OrderedCollection new add: 'Peter'; add: 'John'; add: 'Paul'; yourself. 

Questions

  • Why is this so?
  • What was it done that way?
  • What are the benefits of add: for this?
+8
oop smalltalk squeak pharo visualworks
source share
5 answers

I thought a lot about it. I have never heard that one of the original Smalltalk designers defended this decision, so we don’t know exactly why they did it. I decided that the reason was due to cascades. If add: returned the receiver, then (things add: thing1) add: thing2 will be the same as add: thing1; add: thing2. Adding: return the argument, these two expressions are different from each other, and the programmer can use each when it suits.

However, I think this is a mistake. I have been teaching Smalltalk for over 25 years, and every time I teach it, people have problems with it. I always warn them, but they still make mistakes with the addition of :. Therefore, I consider this a poor design decision.

This design decision is for the library, not the compiler. You can change it by going to collection classes and changing them. Of course, it is impossible to predict how many Smalltalk programs will break. The collections are so fundamental that this change would be as difficult to make as a real change in language.

+12
source share

In other languages ​​you can write:

 b[j] = a[i] = e; 

This is somehow stored in Smalltalk if at:put: returns a put object:

 collectionB at: j put: (collectionA at: i put: e). 

The same interest exists for add: / remove: which allow this type of chain:

 collectionB add: (collectionA add: anElement). collectionB add: (collectionA remove: anElement). 
+6
source share

it is always best to cascade such dispatch methods and never rely on their return values. The same is true with setter methods, sometimes they can return self, sometimes they return a parameter. It is safe to assume that the return value of these methods is close to random and never uses it.

+3
source share

I cannot defend him, and I cannot refute Ralph’s experience.

The pursuit of symmetry could be a factor. Given that #remove: returns an object that has been deleted, it makes sense to have #add: return the added object.

Simple examples dodge us, I think, as well. When we have an object that is already added to a variable, or it is a simple literal, the return value seems meaningless. But if we have (dubious) code that looks like this:

 someProfile add: VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd 

If someProfile is a linear list, I suggested that you can get the added value: 'ed through last. But it can be just a bag or a set. In this case, it may be convenient:

 currentSize := someProfile add: VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd 

Some find it better than:

 someProfile add: (currentSize := VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd) 

Although best of all:

 currentSize := VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd. someProfile add: currentSize 
+2
source share

The best explanation I came up with is to make it equivalent to the job. Suppose you have a code like this:

 (stream := WriteStream on: String new) nextPutAll: 'hello'. stream nextPut: $! 

The purpose of the variable depends on the assigned object. I should be able to replace the variable with the collection and get the equivalent behavior:

 (array at: 1 put: (WriteStream on: String new)) nextPutAll: 'hello'. (array at: 1) nextPut: $! 

Now, having said that, I will punish any developer who wrote this code because it is not readable. I would highlight it on two lines:

 array at: 1 put: (WriteStream on: String new). array first nextPutAll: 'hello'; nextPut: $! 

This is the best excuse I can give. This is done in order to assign collections according to the assignment to variables, but if you use this function, you have code that is difficult to read.

0
source share

All Articles