Why we should remove {} from Squeak

Richard A. O'Keefe ok at atlas.otago.ac.nz
Wed Oct 3 03:29:18 UTC 2001


In as neutral a tone as possible, I would like to direct anyone interested
in the topic of whether {} deserves to be in Squeak or not to
"Eiffel, the Language".

At first glance, Eiffel and Smalltalk are poles apart:
- Eiffel has strong static (well, almost) type checking,
  Smalltalk does not.
- Eiffel has multiple inheritance,
  Smalltalk does not.
- Eiffel has strong support for verifiable design and design-by-contract,
  Smalltalk does not.  (Although now that we have ANSI exception handling,
  there is some encouragement to check things.)
- Eiffel provides feature-by-feature export control (this feature can by
  used by that class and its descendants, this other one by that other one
  and its descendants),
  Smalltalk does not.
- Smalltalk has class variables and class methods,
  Eiffel does not (and I have OFTEN missed class methods).
- Smalltalk has a rich reflective development environment which is not
  easy to distinguish from the runtime environment,
  Eiffel does not.
- Most Smalltalks come with huge class libraries, including multi-platform
  GUI support,
  Eiffel does not (there is the ELKS'95 class library of basic data structures,
  revised in ELKS 2000, but even that is pretty sparse); some Eiffels do have
  medium sized class libraries, but there is less portability than one would
  wish for.

HOWEVER,
- both are "pure" object-oriented languages, even numbers and characters are
  objects.
- both use automatic storage management.
- both have simplified syntaxes.
- both regard source navigation as important (Smalltalk via the IDE,
  Eiffel via "indexing clauses" on classes).
- both have open-source projects (Squeak, SmallEiffel) with enthusiastic
  user communities (although the SmallEiffel mailing list is nowhere near
  as voluminous as the Squeak one).
- both are designed and carried forward by "visionaries" who believe
  passionately that it is possible to develop software FAR better than
  we currently do, although with differing ideas about what that might mean.

I've got a lot of time for Eiffel.  Eiffel and Smalltalk look so different,
feel so different, and yet, and yet, somehow there is a deeper kinship
between them, separating them from C++ and Java.

For a taste of the syntax, here's a "feature" from SmallEiffel's file_tools.e:

   is_readable(path: STRING): BOOLEAN is
         -- True if `path' file exists and is a readable file.
      require
         path /= Void
      do
         std_fr1.connect_to(path)
         Result := std_fr1.is_connected
         if Result then
            std_fr1.disconnect
         end
      end

which we might write in Smalltalk syntax as

    isReadable: path
	"true if there is a readable file called `path'"
	|r|
	path ifNil: [self error: 'nil argument'].
	stdFr1 connectTo: path.
	r := stdFr1 isConnected.
	r ifTrue: [stdFr1 disconnect].
	^r

In "Eiffel, the Language", we're informed that Eiffel syntax changes very
slowly, and that each language feature must not only be useful, but must
solve multiple problems without messing anything else up.  There's an
appendix that lists the changes there have been, and why.

Here's the punchline:  one of the features that has been added to Eiffel
since the first release, which is deemed to fit the rather stringent
"admission" criteria, and to have paid for itself, is "manifest arrays".

If each of e1, ..., en has type T, then
    << e1, ..., en >>
is an expression that constructs a new n-element ARRAY[T] object.
Change << , >> to { . } and this is precisely the {} construct in Smalltalk.

As it happens, the uses to which it is put in Eiffel do not (and could not)
include "case" statements, which Eiffel has in any case special syntax for.

One thing it IS used for is passing a variable number of arguments to a
method.  This is one of the things that {} is used for in Smalltalk.
The first thing I found when I went looking for curly braces was a bunch of

    ^TestOSAPlugin
        doPrimitive: 'primAEDescToString:'
        withArguments: {e1, ..., en}

with varying numbers of parameters.  To be honest, I don't think I'd have
done it that way, but of course we also have #perform:withArguments: for
which {} is admirably suited.  Some calls to #perform:withArguments: in
Squeak 3.0 do use {} syntax, some don't but should, and the majority just
pass on an existing array.

[By the way, when I used Cmd-n to find senders of #perform:withArguments:,
 sometimes the code pane contained both a send of #perform:withArguments:
 and a send of #perform:, and the wrong one was selected.]

I also noted a technique
    {e1. ... en} do: [:x| stuff].
which could certainly be eliminated in favour of
    b := [:x| stuff].
    b value: e1.
    ...
    b value: en.

A similar idiom is possible in Eiffel, details vary depending on what the
library looks like.  The "block" version wasn't possible until "agents"
were added to Eiffel recently, and it still isn't pretty in Eiffel.

I also noted quite a few arrays that _would_ have been literals except
that they contain things such as
 - Points
 - Colors
 - Associations
 - Booleans
which may not appear in an array literal.  The pity of it is that these
arrays get rebuilt over and over again, not because they are mutated, but
simply because there's a choice between inexpressive literals #(...) and
expressive rebuild-every-time expressions {...}.

Of course, in Eiffel, << ... >> is the only array literal notation there is.
The fact that strings are mutable in Eiffel, so that
    x := "abc"
may in fact assign to x a string whose current value is "pqr", causes no
little confusion to beginners; IIRC there was an attempt to work out a new
syntax to reduce this confusion.
A somewhat dated SmallEiffel release I checked had about 1 array literal
per 2 kSLOC; in a way it is surprising that it is so high, because Eiffel
is not a language whose users value conciseness.





More information about the Squeak-dev mailing list