Previous Table of Contents Next


5.2.2. Class Library Features

An extensive and mature class library accompanies all Smalltalk development environments. The libraries vary across versions on implementation of the GUI and external interfacing, but overall they share a significant number of common features. These features are unique to Smalltalk and provide a powerful environment that lends itself to many types of applications. Included in class libraries are multiprocess frameworks, external code interfacing (to C, COBOL, or another), external resource interfacing (such as OLE and OCXs), exception handling, dependency and event frameworks, and reflective capabilities (such as modifying the executing program and querying the structure of an application).

Smalltalk has always been a multiprocess environment. Even on operating systems that don’t support simultaneously executing tasks, Smalltalk provides it’s own internal process framework. As shown in Figure 5.1, an operating system process may contain one or more threads, with one of these threads handling the Smalltalk language processing. The Smalltalk language processing is split among as many Smalltalk processes as the application requires. The scheduling of these processes differs between versions, as does the number of operating system threads. A Smalltalk process is similar to an operating system thread in that it is contained entirely within Smalltalk. However, on platforms that support threads, Smalltalk processes do not become operating system threads. There are some exceptions to this rule:

  The ObjectStudio version of Smalltalk that does offer multiple threads
  An add-in to VisualWorks that provides operating system threads through a different set of calls
  IBM Smalltalk’s coroutine calls, which use a separate operating system thread to make external system calls


FIGURE 5.1.  Smalltalk supports multiple processes, even when the host platform does not.

Often, Smalltalk applications are required to interface with C DLLs, COBOL programs, or other external code modules. Many options are available to interface with these modules, and Smalltalk programmers make use of the facilities to call these external programs. Because most Smalltalk development is done on PCs or workstations, the most common interface of external code is through a C dynamic link library or C shared library. The C interface is the most common external interface supported in Smalltalk, although Smalltalk MVS for IBM mainframes calls external COBOL modules and fits well in that environment.

Smalltalk programs take advantage of many of the same features that other languages use on the host platforms. This can include OCXs for custom GUI controls and OLE for interaction with email and desktop applications under Microsoft operating systems. These features are available in the base Smalltalk products, as well as toolkits from third-party developers. People have interfaced Smalltalk to hardware for communications and control, music and graphics systems, and every conceivable type of other system. Often, an application in Smalltalk is the GUI front end and the “glue” that holds together other separate technologies developed in other languages.

5.2.2.1. Exception Handling

Smalltalk systems include an extremely powerful exception-handling framework. Although the Smalltalk versions differ in syntax and implementation, the common function is to signal errors that occur many layers deep in code, and to respond to those errors in the best layer of application code. For example, in an engineering application, the divide-by-zero error must be handled in any situation in which it might occur. The five possible paths for dealing with an error are shown in Figure 5.2. Choices 4 and 5 create a new stack frame to further execute the other handlers registered previously, while options 1 and 3 drop back one stack frame, and option 2 may drop several. Experienced Smalltalk programmers realize that they must reset any state information changed before the error was encountered when dropping stack frames.


FIGURE 5.2.  Exception handling in Smalltalk provides complex strategies for dealing with errors.

In Smalltalk, you have several options that are implemented based on the code that is executed:

  Resume execution, with a different value, where the error occurred. Sometimes in an engineering calculation, you might want to divide by zero to get an extremely high number (approaching infinity). In other cases, you might want a different equation to be invoked. For example, the following:
   [ (a + b) / c ]
       when: self divideByZeroException
       do: [:signal | signal resumeWith: 99999 ]

replaces the value in the block with 99999, and
   [ (a + b) / c ]
       when: self divideByZeroException
       do: [: signal | signal resumeWith: (a + b) ]

continues execution with the result of a added to b in the stack frame where the error occurred. The use of exception handling does not need to occur within the same set of code or the same class. It can go through many layers of methods and classes and then jump to the exception-handling block elsewhere on the stack when an error is detected.
  Resume execution after the exception-handler block. The expression
   [ (a + b) / c ]
       when: self divideByZeroException
       do: [:signal | signal exitWith: 1 ]

continues execution in the current method with the value of 1 for the expression (a + b) / c.
  Retry execution of the protected code. The expression
   [ (a + b) / c ]
       when: self divideByZeroException
       do: [:signal | signal retry ]

is a poor code example because a divide-by-zero error will not be resolved unless the variables are changing during execution or c is a random number. A retry operation is much more useful when the problem could be due to an external resource that might become available again, such as a file or a network connection that can experience transient disruptions during the normal course of application execution.
  Re-signal the exception for another handler further up the stack. The expression
   [ (a + b) / c ]
       when: self divideByZeroException
       do: [:signal | signal signal]

signals divideByZeroException again and looks for another handler that is further down the stack. If none is found, then the default handler is invoked, which could open the debugger or give the user an error message. This action is usually taken after some logic in the exception handler is unable to resolve the problem.
  Signal a different type of exception. The expression
   [ (a + b) / c ]
       when: self divideByZeroException
       do: [:signal | self badMathError signalWith: signal
   exception ]

provides a new type of signal that is searched for down the stack. Turning one type of exception into another is not frequently done, but it can be useful when creating layers of software that provide a common application programming interface (API) independent of the components used. An example of this is an object translation/brokering mechanism that provides a consistent API and exception interface to the developer while also accommodating an interchangeable database interface with varying API and exception interfaces.

Exception handling is a powerful framework that should be used to deal with unexpected system errors that could happen in your application. It is not good practice to go overboard with this mechanism and create exception handlers for nonexceptional cases (such as application logic). In such a case, the code might become cluttered, less readable, and much harder to maintain.


Previous Table of Contents Next