Previous Table of Contents Next


An exception handler usually completes by returning the value of the handler block in place of the value of the receiver block. (Note that the previous example would return the value of the show: message.) If instead of displaying a message the programmer wanted to return the value 1 when a division by zero occurred, we would rewrite the previous expressions as follows:

  [x / y] on: ZeroDivide do: [:ex| ex resume:1]

This might be used as follows:

  fudgeFactor := [x / y] on: ZeroDivide do: [:ex|ex resume: 1].

If instead of returning a value we want to exit the current method, we can place an explicit return within the handler block:

  fudgeFactor := [x / y] on: Error do: [:ex| ^’not computable’].

Note that in the last example, we specified Error, instead of ZeroDivide, as the exception to be handled. When we specify an exception to be handled, we are really saying that we want to handle the named exception and also any exceptions that are subclasses of the named exception. Because ZeroDivide is a subclass of Error, an attempt to divide by zero or any other error that occurs while evaluating x / y causes the enclosing method to return the string ’not computable’.

An exception is signaled by sending the message signal to the class that defines the exception. For example, the following creates an error exception:

  Error signal

If there is an active handler that deals with the Error exception, it will be executed. Otherwise, the default action for the exception will be executed. It is often useful to provide a textual description when signaling an exception. This is accomplished by sending the message signal: to an exception class:

  Warning signal:  ‘the disk is almost full’

4.6.2. Exception Resumability

A handler block usually completes by executing the final statement of the block. The value of the final statement is then used as the value returned by the exception handler to the sender of the on:do: message. The resumability attribute of an exception determines whether a handler block can return control to the signaler of an exception. A resumable exception can return the message that signaled the exception. A nonresumable exception never returns to the signaler of the exception. For example, the following expression returns 5 as the value of the on:do: message:

  ([Error signal] on: Error do: [:ex| 5]) “returns 5 here”

The next expression returns 5 as the value of the signal message:

  ([Notification signal “returns 5 here”]
      on: Notification do: [:ex| ex resume: 5]).

Resumability is an attribute of an exception, not of the exception handler. An instance of an exception can be explicitly tested to determine whether it is resumable. This is accomplished by sending it the message isResumable. In the following example, the exception handler either returns 5 to the signaler or 10 from the on:do: message, depending on whether the exception class defines a resumable or nonresumable exception:

  [someExceptionClass signal ] on: Error
       do: [:theException|
                   theException isResumable
                        ifTrue: [theException resume: 5]
                        ifFalse: [10]]

4.6.3. Methods of Exiting a Handler Block

Occasionally, it is desirable to conclude processing of a handler block before reaching the final statement of the blocks. This can be accomplished in several ways by sending appropriate messages to the argument of a handler block.

The message exit: causes its argument to be returned as if it were the value of the final statement of the handler blocks. The following two handlers have exactly the same behavior:

  [Error signal] on: Error do: [:ex| 5].
  [Error signal]
      on: Error
      do: [:theException| theException exit: 5].

The most common use of exit: is in a conditional expression of a complex handler block. If the argument of a handler block is a resumable exception, the message resume: can be used in place of exit:. It is an error to send resume: to a nonresumable exception. Doing so causes an exception to be signaled.

Another way to exit a handler block is with the retry message. This message terminates the handler block and retries the evaluation of the receiver of the on:do: block. Any ensure: or ifCurtailed: clean-up blocks created by the original evaluation of the receiver block or by the handler block are executed before the evaluation is retried:

  [^ x / y]
      on: ZeroDivide
      do: [:ex|
            y := 0.000001. “ make the divisor very small but > 0”
            ex retry]

The message retryUsing: is similar to retry, but instead of evaluating the original receiver block, the argument to retryUsing: is evaluated in its place:

  [self doSomeTaskTheFastWay]
      on: LowMemory
      do: [:ex|
          ex retryUsing: [self  doSomeTaskTheSpaceEfficientWay]]

The occurrence of an exception most frequently causes Smalltalk to discard the block evaluation currently in progress. Sometimes a method does something that requires a subsequent action, regardless of whether an exception occurs. A good example is a method that places a lock on an external file. As long as the lock is in place, other users cannot access the file. Such a lock must be released even if an exception occurs. The following is an example of a method that might be written in a class that supports file access to implement such behavior:

  whileLockedDo: aBlock
      “Lock the receiving file.  Process the argument block while the
      file is locked, then unlock the file.  Return the value of the
      argument block. Be sure to release the lock if an exception
      occurs.”
      self lock.  “set the lock”
      ^aBlock ensure: [self unlock “clear the lock”]


Previous Table of Contents Next