Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
The most recent call is the one nearer the top of the screen. In the next example, we will cause an error which happens deeper within an object.
8.2. Nested Calls
Type the following lines:
Smalltalk at: #x put: (Dictionary new) !
x at: 1 !
The error you receive will look like:
Dictionary new: 32 "<0x33788>" error: key not found MethodContext>>#value
Dictionary>>#at:ifAbsent:
Dictionary>>#at
UndefinedObject>>#executeStatements
UndefinedObject>>nil
The error itself is pretty clear; we asked for something within the Dictionary which wasn’t there. The object which had the error is identified as "Dictionary new: 32". A Dictionary’s default size is 32; thus, this is the object we created with "Dictionary new".
The stack backtrace shows us the inner structure of how a Dictionary responds to the at: message.
Our hand-entered command causes the usual two entries for "UndefinedObject". Then we see a Dictionary object responding to an "at:" message (the "Dictionary>>#at" line). This code called the object with an
"at:ifAbsent:" message. All of a sudden, a different object receives a "value" message, and then the error happened.
This isn’t quite true; the error happened in the Dictionary object. The mystery is where this
"MethodContext" came from. Fortunately, it isn’t much of a mystery. The answer lies in what we covered
-24-
in the last two chapters: code blocks.
A very common way to handle errors in Smalltalk is to hand down a block of code which will be called when an error occurs. For the Dictionary code, the "at:" message passes in a block of code to the at:ifAbsent: code to be called when "at:ifAbsent:" can’t find the given key. Thus, without even looking at the code for Dictionary itself, we can guess that the Dictionary "at:" message handling looks something like:
at: key ifAbsent: errCodeBlock
...look for key...
(keyNotFound) ifTrue: [ ˆ(errCodeBlock value) ]
...
at: key
ˆself at: key ifAbsent: [ˆself error: ’key not found’]
The key is that we see in the stack backtrace that at:ifAbsent: is called from at:, and a MethodContext is called to give the error. Once we realize that a MethodContext is just a fancy name for a code block, we can guess that at: handed in a code block to print an error, and at:ifAbsent: used the standard "value" message to invoke this code block when it realized that the requested key couldn’t be found in the Dictionary.
It would be nice if each entry on the stack backtrace included source line numbers. Unfortunately, at this point GNU Smalltalk doesn’t provide this feature. Of course, you have the source code available....
8.3. Some Shortcomings in GNU Smalltalk
Unfortunately, there are some errors which GNU Smalltalk is not very helpful in detecting. This information applies to the latest version currently available—GNU Smalltalk 1.1.1. Try indexing something which isn’t any sort of Collection:
(7 at: 99) printNl !
One would expect to receive an error,20 but instead GNU Smalltalk simply returns the receiving object—7.
Similarly, one would expect Array bounds to be checked:
Smalltalk at: #x put: (Array new: 10) !
(x at: 7 put: 123) printNl !
(x at: 11 put: 1234) printNl !
(x at: 7) printNl !
(x at: 11) printNl !
But this example returns no error in GNU Smalltalk.21 When an assignment to an array slot is correct, the returned value is the value assigned—123 in this case. When you assign outside the array bounds, you will receive the array object itself as the result! Thus, if you accidentally index your array incorrectly, you will have to figure out what happened from a place further in your code where an error crops up because your code was trying to operate upon an element of the array, but instead is working on the array itself.
8.4. Looking at Objects
When you are chasing an error, it is often helpful to examine the instance variables of your objects.
While strategic "printNl"s will no doubt help, you can look at an object without having to write all the code yourself. The "inspect" message works on any object, and dumps out the values of each instance variable within the object. Thus:
Smalltalk at: #x put: (Interval from: 1 to: 5) !
x inspect !
displays:
20 And in fact, you will receive an error in most Smalltalk systems.
21 Again, it will error in most other Smalltalk implementations.
-25-
An instance of Interval
start: 1
stop: 5
step: 1
There’s one thing the object inspector doesn’t display—the contents of an indexed class. Since we haven’t looked at this kind of object yet, we’ll leave this to its own chapter.
We’ll finish this chapter by emphasizing a technique which has already been covered—the use of the
"error:" message in your own objects. As you saw in the case of Dictionary, an object can send itself an error: message with a descriptive string to abort execution and dump a stack backtrace. You should plan on using this technique in your own objects. It can be used both for explicit user-caused errors, as well as in internal sanity checks.
-26-
9. Coexisting in the Class Hierarchy
The early chapters of this paper discussed classes in one of two ways. The "toy" classes we developed were rooted at Object; the system-provided classes were treated as immutable entities. While one shouldn’t modify the behavior of the standard classes lightly, "plugging in" your own classes in the right place among their system-provided brethren can provide you powerful new classes with very little effort.
This chapter will create two complete classes which enhance the existing Smalltalk hierarchy. The discussion will start with the issue of where to connect our new classes, and then continue onto implementation. Like most programming efforts, the result will leave many possibilities for improvements. The framework, however, should begin to give you an intuition of how to develop your own Smalltalk classes.