[prev] [up] [next]

Coding Style used in Smalltalk/X Classes

Contents

Introduction

This document describes the coding style and conventions used in Smalltalk/X's class library.
The author is aware of the fact, that coding style is a very personal matter and should not be enforced by dictators.
However, it is useful to follow some rules, to enable other programmers an easier entry into the system. Also, there exist tools which extract useful information and can format neat documents if you follow those rules in your classes. Thus, when its about time to deliver a documentation on your project, a whole bunch of work done for free ...
Experienced Smalltalk programmers may want to skip this document.

If you have any suggestions or additions on this theme, let me know about it.

I admit that some of Smalltalk/X's code does not follow those guidelines: some code is very old and the author(s) of the code have matured as we all do.
Bad code is and will be refactored, whenever we encounter it.

Class Documentation

In Smalltalk/X, every class contains a method category called "documentation" in its class protocol. You will find at least two methods named #version and #documentation there:

The #version method

The #version methods comment consists of a single version line - this is created automatically by the source code control system (RCS or SCCS).

NEVER manually change this string in the browser - ST/X depends on it, in order to be able to extract a classes correct source code from the source repository.

If you report an error (to eXept), this string should be used to identify the exact version of the class.

The #documentation method

The #documentation methods comment describes the class, its uses and (if of public interest) its instance and classvariables.

In many classes, you will find an addition #examples method. This will contain a comment giving typical uses (often ready to select & doIt).

These methods consist of comments only; they are not meant to be executed. (actually, if evaluated, they will return the receiver; since empty methods are semantically equivalent to a '^ self' method).

The SystemBrowser automatically shows the documentation text (found either in the "documentation" method or in the class comment) whenever a class is selected. Thus, to be nice to other people browsing through the system, you should add a short description of what your class is about in these documentation methods.

Also, the document viewer can extract a classes documentation methods text and present it cutely formatted - you get your documentation almost for free, if you stick to these conventions !

Do not worry about memory usage when creating documentation methods - simple methods which return self (as empty methods do) all share a common piece of code, so there will NOT be thousands of empty methods filling up your memory. (to be exact: there is some little overhead per method created by the method object itself - not by the methods code. However, for production code, all documentation methods can be easily removed. Also, stc provides a command line argument, to skip all methods in the documentation category; to allow building more compact class libraries.)

If you don't like the above, use the classes comment string, which also does not eat up memory (it used to before release 2.10.3 of Smalltalk/X).

BTW: from the authors experience, you should not delay documentation too much. Write them down as soon as possible - otherwise you may not find the time to do so later - or you may simply forget to do it. Also, keep in mind that it may take more time to add those comments later, since you may have to reflect about what is going on. From our experience, the later the documentation is written in a project, the higher is its cost.

Class Version

If you plan to use the Smalltalk/X sourcecode manager, every class should contain a #version method, which must return the classes version string.
The actual format of that string is specific to the underlying sourceCode mechanism (for rcs or cvs, it is something like: "$Header$" ; for sccs, it is: "%W% %H%").
These version strings will be expanded (by the source code management) to the actual version; for example, in the Array class, you will see something like:
version
    ^ '$Header: /cvs/stx/stx/doc/online/english/programming/codingStyle.html,v 1.20 2003/03/26 15:40:04 cg Exp $'

Notice:
Currently only a manager for cvs is provided - you have to write your own manager classes (derive from AbstractSourceCodeManager) if others base mechanisms (for example: sccs) are to be used.

If such a version method is present, and the sourceCodeManager is enabled, access to the repository is possible from the browser, and done automatically to retrieve a classes source (based on the actual version of that class in the system).

When classes are checked in via the browser, these version methods are automatically created (if not already present) - there is also a menu function, to create those manually.

In normal operation, the handling of those is transparent, and you can safely forget about it ... its useful to know about it, anyway).

Method Documentation

Every method should contain (at least) two comments: Example (from Collections enumeration protocol):
    select:aBlock
	"return a new collection 
	 with all elements from the receiver, 
	 for which the argument aBlock evaluates to true"

	|newCollection|

	newCollection := self species new.
	self do:[:each |
	    (aBlock value:each) ifTrue:[
		newCollection add:each
	    ].
	].
	^ newCollection

	"
	 #(1 2 3 4) select:[:e | e odd]   
	 (1 to:10) select:[:e | e even]     
	"

Variable and Method Naming

Of course, you should give your variables and methods descriptive names. You should do so in any programming language. In smalltalk, a common trick is to encode the expected type of a variable in the name (which you don't have to in static typed languages). For example, names like "originPoint", "lineString" or "collectedNames" make it totally clear, what the variables/arguments are used for.

By convention, global variables and class variables should start with an upper case character - other variables and selectors by a lower case chracter.

Global Variables

Think twice before using globals - usually there is no need for them.
Beside increasing code complexity (by introducing side effects), use of globals may lead to conflicts if packages from different programming teams are merged and both use the same global name. Although the browser offers search functions for uses of globals, you have to manually edit (and think about) the code in this case. Avoid this by banning globals from your code.

In many situations, a global can be eliminated by by passing additional method arguments (which may even be an advantage later, offering more possibilities for reuse of a method).
Almost all globals can easily be replaced by a private classVariable instead and access be provided to other parts via class methods.

Code Comments

You won't need too many comments in your methods, if the code is clean and straight forward. Don't add comments just for the comment.
For example, a comment like:
	sum := sum + 1.         "add one to sum"
is stupid and filling your methods with this kind of "information" actually makes your code less readable.
(you may wonder why this is mentioned here; we have seen departments where code ``quality'' was measured by counting comments, which ended in people doing above rubbish - only to make the codecheckers happy.)

Also, if you think that a variable needs a comment stating its use, think about changing the variables name;
For example, the following code is a (stupid) example for a bad variable name:

	|c "counter for blabla"|
why not name the variable "counterForBlaBla" right away ?

And, similar, if a group of statements need an explanation as in:

	...
	"read the data from file blabla"
	...code to read data...
	...and so on...
	...
I would suggest that you extract those statements into a separate method, name it "readDataFromFileBlaBla" and invoke that method.
Voila - the methods name is just as good as the original comment.

The above actually means, that as your code becomes more more readable and less-cryptic, less comments are needed.
However, if you use special tricks or uncommon constructs, you should add a comment describing what is going on - for yourself and for others.

In the documentation you may see comments explaining obvious things (like: " ... now open the view ...") which seems to violate the above.
The reason is that we expect these texts to be read also by newcomers - therefore, more than usual is often commented there.

Code Indentation

The question of how code should be indented is a very subjective and the discussion often even a religious one. For that reason, we will not give any recommendations here. Instead, the two most commonly used styles are described in short here.
Take the one that you (and your friends) find to be the easiest to read.

Readability is usually better if you do not have to scroll when looking at a methods code. Therefore, methods should be short. On the other hand, don't break up a method into many short methods just for this; if a methods functionality requires more than a page of code, so be it.

Many other styles are possible, however, whichever you choose, follow these rules:

Currently, Smalltalk/X's codeViews do not support automatic indentation of program text. This will be added in a later release. However, it is still open, which styles will be supported by this autoindent function.

Avoid obfuscated code

The following is an incomplete list of recommendations:

Things you should not do

Do not hardwire knowledge about object relations into you program; a typical example (which is very bad) is the following extract from a piece of code found in the manchester archive:
    ...
    aReadStream ioConnection input readWait.
    ^ aReadStream atEnd not
    ...
obviously, the readStream object is some kind of Socket-accessing thingy, and the programmer wants to wait for something to arrive.
The bad thing with the above, that it hardwires the knowledge of the (internal) layout of that socket-accessing thingy into the program.

Another example is:

    ...
    widget wrapper wrapper wrapper controller enable.
    ...
here, the widgets internal hierarchy is reflected in the access-path, and the code will have a very hard time if the wrapper hierarchy ever changes.

These code fragments were specially written for a parcPlace system, but even parcPlace will not be able to change any internals, without affecting this program.
Thus, there is a chance that the above code will not work without change in the next parcPlace version (we dont even think of porting it without change to another smalltalk dialect).

A better solution was to provide a #readWait in the class of the socket-accessing object, and delegate things there, and to provide an enable in the widget, which delegates it to some controller.

This hides the object structure and allows both parcPlace to change things, and allows ST/X's Socket class to behave like other SocketAccessors.

Of course, the above examples do not only apply to system classes - the same is often found in user code (i.e. GUI code).


Copyright © 1995 Claus Gittinger Development & Consulting

<cg@exept.de>

Doc $Revision: 1.20 $ $Date: 2003/03/26 15:40:04 $