Why we should remove {} from Squeak

Richard A. O'Keefe ok at atlas.otago.ac.nz
Wed Oct 3 06:54:50 UTC 2001


	1) {}'s encourage poor object-oriented programming style. Our experience 
	has shown that many situations where dynamically constructed arrays are 
	returned from a method, the programmer really should have defined a new 
	class to represent the result. (See 
	http://www.smalltalksystems.com/publications/misuse_of_arrays.doc (requires 
	MS Word))

Can someone make a Postscript or PDF version of this and put it somewhere?

	In this regard, {}'s are an attractive nuisance, particularly
	for novice programmer who should be learning to define new
	abstractions rather than using arrays.

This is the old "REAL oopy methods never return more than one result"
religion again, and I for one don't believe it.  For an extreme version
of this dogma, see Eiffel, where a method that has a side effect is not
supposed to return a result, so reading looks like
	source.try_to_read
	if source.read_worked then
	   ... source.last_thing_read ...
Lisp, Scheme, Prolog, Mercury, and Mesa all take the view that there is
nothing outlandish about returning more than one result.  In ML, Haskell,
and Clean, I find myself returning a heck of a lot of tuples, which the
compiler then works hard to eliminate in favour of _really_ returning
more than one value.

Here's a favourite example of mine.  In my XML kit, I have a function
that finds a place in a tree and splits it at that point.  Conceptually,
it's quite simple:

    split_before :: (Tree -> Bool) Tree -> (Tree, Tree)
    split_after  :: (Tree -> Bool) Tree -> (Tree, Tree)

If I followed the Eiffel dogma, I'd have to write
	tree1.split_before(condition)
	tree2 := tree2.piece_split_off
which requires every tree to have a place to hold a piece split off.
In Smalltalk, I have a choice of

(a) tree2 := tree1 splitBefore aBlock.
(b) aPair := tree1 splitBefore aBlock.
    tree1 := aPair first.
    tree2 := aPair second.    
(c) anArray := tree1 splitBefore aBlock.
    tree1 := anArray first.
    tree2 := anArray second.

each of which has flaws:

(a) => I have to make an entirely arbitrary decision about which part of
    the tree counts as "the same" tree afterwards and which part counts as
    "the new" tree.  Conceptually, the old tree has disappeared, and two
    new trees exist, the interface forces me to make this choice.

(b) => The Pair class has NO semantics other than to hold a pair of things.
    (Trees in this case.)  There just isn't anything to add.  (No, there
    isn't any general "here is a pair of trees, glue them together"
    operation in this case.  The bleeding edges have to match up.)

(c) => There is the possibility of an Array result having too few or too
    many elements.

This argument against {} is futile, because there is nothing to stop
someone defining Pair and Triple classes (or any other n-tuple they need)
and using those.  Indeed, I mailed just such classes to this mailing list
early last month as part of the support for
    aCollection with: anotherCollection [with: yetAnotherCollection]

It is not clear to me that replacing
    {left. right}
with
    Pair first: left second: right
is much of a gain.

	2)  The most common reasonable use of {}'s seems to be in grouping a set of 
	similar items to be passed as a single method argument. This is really just 
	a poor man's way to implement a variable length method argument list. There 
	are interesting proposals that have been made for adding variable length 
	argument lists to Smalltalk. If one of these were implemented, the more 
	general {} construct would not be needed.
	
Yes, but no such construct DOES exist in ANSI Smalltalk or Squeak.	
This is like saying "You don't need a loaf of bread, a cake would be much
better.  And no, I'm NOT going to give you a cake."  Feh!
	
One is also obliged to admit that Smalltalk has a lot of code in the
system, starting with #perform:withArguments:, that likes to put arguments
in arrays.

One should also note that passing arrays allows an argument list to vary
in several ways, not just one.  In Lisp terms, you can have as many
&rest arguments as you want.

There are two other uses in Squeak, the first of which seems to be very
common, and seems to me entirely reasonable:

(3) Creating an array that would have been an array literal had array
    literals allowed Points, Colors, Booleans, and so on as elements.
    (To be sure, Points are mutable, but so are strings, and array
    literals allow them.)

(4) The caseOf: construct.

{} syntax is comparatively easy to support in a compiler.
It doesn't need any new methods in the collection classes,
no new collection-class-related classed, not even any new node
types, just an additional parsing method or two.
When you see {e1. ... en} just construct the same nodes that would have
been constructed had
((Array basicNew: n) basicAt: 1 put: (e1); ...
 basicAt: n put: (en); yourself)
appeared in its place.  (Yes, this is an argument that caseOf: should
work with this pattern, identically to a curly braced form.)

This means that once people get used to {} and find idioms where it
expresses things that are awkward to express otherwise, it's going to
be hard to stop projects forcibly adding it to their compiler if it
isn't there, short of putting the compiler behind a brick wall.





More information about the Squeak-dev mailing list