The goal of this chapter is to show you how to develop programs in the Pharo programming environment. You have already seen how to define methods and classes using the browser; this chapter will show you more of the features of the browser, and introduce you to some of the other browsers.
Of course, very occasionally you may find that your program does not work as you expect. Pharo has an excellent debugger, but like most powerful tools, it can be confusing on first use. We will walk you through a debugging session and demonstrate some of the features of the debugger.
One of the unique features of Smalltalk is that while you are programming, you are living in a world of live objects, not in a world of static program text. This makes it possible to get very rapid feedback while programming, which makes you more productive. There are two tools that let you look at, and indeed change, live objects: the inspector and the explorer.
The consequence of programming in a world of live objects rather than with files and a text editor is that you have to do something explicit to export your program from your Smalltalk image. The old way of doing this, also supported by all Smalltalk dialects, is by creating a fileout or a change set, which are essentially encoded text files that can be imported into another system. The new way of doing this in Pharo is to upload your code to a versioned repository on a server. This is done using a tool called Monticello, and is a much more powerful and effective way to work, especially when working in a team.
Smalltalk and modern graphical interfaces were developed together. Even before the first public release of Smalltalk in 1983, Smalltalk had a self-hosting graphical development environment, and all Smalltalk development was taking place in it. Let’s start by looking at the main tools in Pharo.
The Debugger has an obvious role, but you will discover that it has a more central place compared to debuggers for other programming languages, because in Smalltalk you can program in the debugger. The debugger is not launched from a menu; it is normally entered by running a failing test, by typing CMD–. to interrupt a running process, or by inserting a self halt expression in code.
Many different class browsers have been developed over the years for Smalltalk. Pharo simplifies this story by offering a single browser that integrates various views. Figure 6.1 shows the browser as it appears when you first open it.1
The four small panes at the top of the browser represent a hierarchic view of the methods in the system, much in the same way as the NeXTstep File Viewer and the Mac OS X Finder in column mode provide a view of the files on the disk. The leftmost pane lists packages of classes; select one (say Kernel) and the pane immediately to the right will then show all of the classes in that package.
Similarly, if you select one of the classes in the second pane, say, Model (see Figure 6.2), the third pane will show all of the protocols defined for that class, as well as a virtual protocol --all--, which is selected by default. Protocols are a way of categorizing methods; they make it easier to find and think about the behaviour of a class by breaking it up into smaller, conceptually coherent pieces. The fourth pane shows the names of all of the methods defined in the selected protocol. If you then select a method name, the source code of the corresponding method appears in the large pane at the bottom of the browser, where you can view it, edit it, and save the edited version. If you select class Model , protocol dependents and the method myDependents , the browser should look like Figure 6.3.
Unlike directories in the Mac OS X Finder, the four top panes of the browser are not quite equal. Whereas classes and methods are part of the Smalltalk language, packages and protocols are not: they are a convenience introduced by the browser to limit the amount of information that needs to be shown in each pane. For example, if there were no protocols, the browser would have to show a list of all of the methods in the selected class; for many classes this list would be too large to navigate conveniently.
Because of this, the way that you create a new package or a new protocol is different from the way that you create a new class or a new method. To create a new package, action-click in the package pane and select new package ; to create a new protocol, action-click in the protocol pane and select new protocol . Enter the name of the new thing in the dialog, and you are done: there is nothing more to a package or a protocol than its name and its contents.
In contrast, to create a new class or a new method, you will actually have to write some Smalltalk code. If you click the currently selected package (in the left-most pane), the bottom browser pane will display a class creation template (Figure 6.4). You create a new class by editing this template: replace Object by the name of the existing class of which you wish to create a subclass, replace NameOfSubclass by the name that you would like to give to your new subclass, and fill in the instance variable names if you know them. The category for the new class is by default the category of the currently selected package2 , but you can change this too if you like. If you already have the browser focussed on the class that you wish to subclass, you can get the same template with slightly different initialization by action-clicking in the class pane, and selecting class templates … ⊳ subclass template . You can also just edit the definition of an existing class, changing the class name to something new. In all cases, when you accept the new definition, the new class (the one whose name follows the #) is created (as is the corresponding metaclass). Creating a class also creates a global variable that references the class, which is why you can refer to all of the existing classes by using their names.
Can you see why the name of the new class has to appear as a Symbol (i.e., prefixed with #) in the class creation template, but after the class is created, code can refer to the class by using the name as an identifier (i.e., without the #)?
The process of creating a new method is similar. First select the class in which you want the method to live, and then select a protocol. The browser will display a method-creation template, as shown in Figure 6.5, which you can fill-in or edit.
The browser provides several tools for exploring and analysing code. These tools can be accessed by action-clicking in the various contextual menus, or, in the case of the most frequently used tools, by means of keyboard shortcuts.
Sometimes you want to open multiple browser windows. When you are writing code you will almost certainly need at least two: one for the method that you are typing, and another to browse around the system to see how things work. You can open a browser on a class named by any selected text using the CMD–b keyboard shortcut.
Try this: in a workspace window, type the name of a class (for instance Morph), select it, and then press CMD–b. This trick is often useful; it works in any text window.
Action-clicking browse … ⊳ senders (n) in the method pane will bring up a list of all methods that may use the selected method. With the browser open on Morph, click on the drawOn: method in the method pane; the body of drawOn: displays in the bottom part of the browser. If you now select senders (n) (Figure 6.6), a menu will appear with drawOn: as the topmost item, and below it, all the messages that drawOn: sends (Figure 6.7). Selecting an item in this menu will open a browser with the list of all methods in the image that send the selected message (Figure 6.8).
The “n” in senders (n) tells you that the keyboard shortcut for finding the senders of a message is CMD–n. This will work in any text window.
Select the text “drawOn:” in the code pane and press CMD–n to immediately bring up the senders of drawOn:.
If you look at the senders of drawOn: in AtomMorph»drawOn:, you will see that it is a super send. So we know that the method that will be executed will be in AtomMorph’s superclass. What class is that? Action-click browse ⊳ hierarchy implementors and you will see that it is EllipseMorph.
Now look at the sixth sender in the list, Canvas»draw, shown in Figure 6.8. You can see that this method sends drawOn: to whatever object is passed to it as an argument, which could potentially be an instance of any class at all. Dataflow analysis can help figure out the class of the receiver of some messages, but in general, there is no simple way for the browser to know which message-sends might cause which methods to be executed. For this reason, the “senders” browser shows exactly what its name suggests: all of the senders of the message with the chosen selector. The senders browser is nevertheless extremely useful when you need to understand how you can use a method: it lets you navigate quickly through example uses. Since all of the methods with the same selector ought to be used in the same way, all of the uses of a given message ought to be similar.
The implementors browser works in a similar way, but instead of listing the senders of a message, it lists all of the classes that implement a method with the same selector. To see this, select drawOn: in the method pane and select browse ⊳ implementors (m) (or select the “drawOn:” text in the code pane and press CMD–m). You should get a method list window showing a scrolling list of the 90-odd classes that implement a drawOn: method. It shouldn’t be all that surprising that so many classes implement this method: drawOn: is the message that is understood by every object that is capable of drawing itself on the screen.
When you save a new version of a method, the old one is not lost. Pharo keeps all of the old versions, and allows you to compare different versions and to go back (“revert”) to an old version.
The browse ⊳ versions (v) menu item gives access to the successive modifications made to the selected method. In Figure 6.9 we can see two versions of the buildWorldMenu: method.
The top pane displays one line for each version of the method, listing the initials of the programmer who wrote it, the date and time at which it was saved, the names of the class and the method, and the protocol in which it was defined. The current (active) version is at the top of the list; whichever version is selected is displayed in the bottom pane. Buttons are also provided for displaying the differences between the selected method and the current version, and for reverting to the selected version.
The existence of the versions browser means that you never have to worry about preserving code that you think might no longer be needed: just delete it. If you find that you do need it, you can always revert to the old version, or copy the needed code fragment out of the old version and paste it into a another method. Get into the habit of using versions; “commenting out” code that is no longer needed is a bad practice because it makes the current code harder to read. Smalltalkers rate code readability extremely highly.
HINT What if you delete a method entirely, and then decide that you want it back? You can find the deletion in a change set, where you can ask to see versions by action-clicking. The change set browser is described in Section 6.8
The inheritance browser displays all the methods overridden by the displayed method. To see how it works, select the ImageMorph»drawOn: method in the browser. Note the triangular icons next to the method name (Figure 6.10). The upward-pointing triangle tells you that ImageMorph»drawOn: overrides an inherited method (i.e., Morph»drawOn:), and the downward-pointing triangle tells you that it is overridden by subclasses. (You can also click on the icons to navigate to these methods.) Now select browse ⊳ inheritance . The inheritance browser shows you the hierarchy of overridden methods (see Figure 6.10).
By default, the browser presents a list of packages in the leftmost pane. However it is possible to switch to a class hierarchy view. Simply select a particular class of interest, such as ImageMorph and then click on the hier. button. You will then see in the left-most pane a class hierarchy displaying all superclasses and subclasses of the selected class. The second pane lists the packages implementing methods of the selected class. In Figure 6.11, the hierarchy browser reveals that the direct superclass of ImageMorph is Morph.
By action-clicking on a class in the class pane, and selecting browse ⊳ chase variables , you can find out where an instance variable or a class variable is used. You will be presented with a chasing browser that will allow you to walk through the accessors of all instance variables and class variables, and, in turn, methods that send these accessors, and so on (Figure 6.12).
The various ⊳ view … menu item available by action-clicking in the method pane brings up the “how to show” menu, which allows you to choose how the browser shows the selected method in the source pane. Options include the source code, prettyPrint ed source code, byteCode and source code decompile d from the byte codes.
Note that selecting prettyPrint in the “how to show” menu is not the same as pretty printing a method before you save it3 . The menu controls only how the browser displays, and has no effect on the code stored in the system. You can verify this by opening two browsers, and selecting prettyPrint in one and source in the other. In fact, focussing two browsers on the same method and selecting byteCode in one and decompile in another is a good way to learn about the Pharo virtual machine’s byte-coded instruction set.
The contextual menus offer a large number of standard refactorings. Simply action-click in any of the four panes to see the currently available refactoring operations. See Figure 6.13.
Refactoring was formerly available only in a special browser called the refactoring browser, but it can now be accessed from any browser.
Many additional functions are available by action-clicking in the browser panes. Even if the labels on the menu items are the same, their meaning may be context dependent. For example, the package pane, the class pane, the protocol pane and the method pane all have a file out menu item. However, they do different things: the package pane’s file out menu files out the whole package, the class pane’s file out menu files-out the whole class, the protocol pane’s file out menu files out the whole protocol, and the method pane’s file out menu files-out just the displayed method. Although this may seem obvious, it can be a source of confusion for beginners.
Possibly the most useful menu item is find class…(f) in the package pane. Although the categories are useful for the code that we are actively developing, most of us do not know the categorization of the whole system, and it is much faster to type CMD–f followed by the first few characters of the name of a class than to guess which package it might be in. recent classes… can also help you quickly go back to a class that you have browsed recently, even if you have forgotten its name.
You can also search for a specific class or method by typing the name into the query box at the top left of the browser. When you enter return, a query will be posed on the system, and the query results will be displayed. Note that by prefixing your query with #, you can search for references to a class or senders of a message. If you are searching for a particular method of the selected class, it is often quicker to browse the --all-- protocol (which is the default), place the mouse in the method pane, and type the first letter of the name of the method that you are looking for. This will usually scroll the pane so that the sought-for method name is visible.
Try both ways of navigating to OrderedCollection»removeAt:
There are many other options available in the menus. It pays to spend a few minutes working with the browser and seeing what is there.
Compare the result of Browse Protocol , Browse Hierarchy , and Show Hierarchy in the class pane menu.
The class SystemNavigation provides a number of utility methods that are useful for navigating around the system. Many of the functions offered by the classic browser are implemented by SystemNavigation.
Open a workspace and evaluate the following code to browse the senders of drawOn::
SystemNavigation default browseAllCallsOn: #drawOn:
To restrict the search for senders to the methods of a specific class:
SystemNavigation default browseAllCallsOn: #drawOn: from: ImageMorph
Because the development tools are objects, they are completely accessible from programs and you can develop your own tools or adapt the existing tools to your needs.
The programmatic equivalent to the implementors menu item is:
SystemNavigation default browseAllImplementorsOf: #drawOn:
To learn more about what is available, explore the class SystemNavigation with the browser. Further navigation examples can be found in the FAQ (Appendix A).
We gave you a quick overview of Monticello, Pharo’s packaging tool, in Section 2.9. However, Monticello has many more features than were discussed there. Because Monticello manages packages, before telling you more about Monticello, it’s important that we first explain exactly what a package is.
We have pointed out earlier, in Section 2.3 that packages are more or less equivalent to categories. Now we will see exactly what the relationship is. The package system is a simple, lightweight way of organizing Smalltalk source code that exploits a simple naming convention for categories and protocols.
Let’s explain this using an example. Suppose that you are developing a framework named to facilitate the use of relational databases from Pharo. You have decided to call your framework PharoLink, and have created a series of categories to contain all of the classes that you have written, e.g., category ’PharoLink-Connections’ contains OracleConnection MySQLConnection PostgresConnection and category ’PharoLink-Model’ contains DBTable DBRow DBQuery, and so on. However, not all of your code will reside in these classes. For example, you may also have a series of methods to convert objects into an SQL-friendly format:
These methods belong in the same package as the classes in the categories PharoLink-Connections and PharoLink-Model. But clearly the whole of class Object does not belong in your package! So you need a way of putting certain methods in a package, even though the rest of the class is in another package.
The way that you do this is by placing those methods in a protocol (of Object, String, Date, and so on) named *PharoLink (note the initial asterisk). The combination of the PharoLink-… categories and the *PharoLink protocols form a package named PharoLink. To be precise, the rules for what goes in a package are as follows.
A package named Foo contains:
A consequence of these rules is that each class definition and each method belongs to exactly one package. The except in the last rule has to be there because those methods must belong to other packages. The reason for ignoring case in rule 2 is that, by convention, protocol names are typically (but not necessarily) lower case (and may include spaces), while category names use CamelCase (and don’t include spaces).
The class PackageInfo implements these rules, and one way to get a feel for them is to experiment with this class.
Evalute the following expression in a workspace:
mc := PackageInfo named: ’Monticello’
It is now possible to introspect on this package. For example, printing mc classes in the workspace pane will return the long list of classes that make up the Monticello package. mc coreMethods will return a list of MethodReferences for all of the methods in those classes. mc extensionMethods is perhaps one of the most interesting queries: it will return a list of all methods contained in the Monticello package but not contained within a Monticello class.
Packages are a relatively new addition to Pharo, but since the package naming conventions were based on those already in use, it is possible to use PackageInfo to analyze older code that has not been explicitly adapted to work with it.
Print (PackageInfo named: ’Collections’) externalSubclasses; this expression will answer a list of all subclasses of Collection that are not in the Collections package.
Monticello is named after the mountaintop home of Thomas Jefferson, third president of the United States and author of the Statute of Virginia for Religious Freedom. The name means “little mountain” in Italian, and so it is always pronounced with an Italian “c”, which sounds like the “ch” in chair: Mont-y’-che-llo.
When you open the Monticello browser, you will see two list panes and a row of buttons, as shown in Figure 6.14. The left-hand pane lists all of the packages that have been loaded into the image that you are running; the particular version of the package is shown in parentheses after the name.
The right-hand pane lists all of the source-code repositories that Monticello knows about, usually because it has loaded code from them. If you select a package in the left pane, the right pane is filtered to show only those repositories that contain versions of the selected package.
One of the repositories is a directory named package-cache, which is a sub-directory of the directory in which your image is running. When you load code from or write code to a remote repository, a copy is also saved in the package cache. This can be useful if the network is not available and you need to access a package. Also, if you are given a Monticello (.mcz) file directly, for example as an email attachment, the most convenient way to access it is to place it in the package-cache directory.
To add a new repository to the list, click the +Repository , and choose the kind of repository from the pop-up menu. Let’s add an HTTP repository.
Open Monticello, click on +Repository , and select HTTP . Edit the dialog to read:
Then click on Open to open a repository browser on this repository. You should see something like Figure 6.15. On the left is a list of all of the packages in the repository; if you select one, then the pane on the right will show all of the versions of the selected package in this repository.
If you select one of the versions, you can Browse it (without loading it into your image), Load it, or look at the Changes that will be made to your image by loading the selected version. You can also make a Copy of a version of a package, which you can then write to another repository.
As you can see, the names of versions contain the name of the package, the initials of the author of the version, and a version number. The version name is also the name of the file in the repository. Never change these names; correct operation of Monticello depends on them! Monticello version files are just zip archives, and if you are curious you can unpack them with a zip tool, but the best way to look at their contents is using Monticello itself.
To create a package with Monticello, you have to do two things: write some code, and tell Monticello about it.
Create a package called PBE-Monticello, and put a couple of classes in it, as shown in Figure 6.16. Also, create a method in an existing class, such as Object, and put it in the same package as your classes, using the rules from page 282 — see Figure 6.17.
To tell Monticello about your package, click on +Package , and type the name of the package, in this case “PBE”. Monticello will add PBE to its list of packages; the package entry will be marked with an asterisk to show that the version in the image has not yet been written to any repository. Note that you now should have two packages in Monticello, one called PBE and another called PBE-Monticello. That’s alright because PBE will contain PBE-Monticello, and any other packages starting with PBE-.
Initially, the only repository associated with this package will be your package cache, as shown in Figure 6.18. That’s OK: you can still save the code, which will cause it to be written to the package cache. Just click Save and you will be invited to provide a log message for the version of the package that you are about to save, as shown in Figure 6.19; when you accept the message, Monticello will save your package. To indicate this, the asterisk decorating the name in Monticello’s package pane will be removed, and the version number added.
If you then make a change to the package — say by adding a method to one of the classes — the asterisk will re-appear, showing that you have unsaved changes. If you open a repository browser on the package cache, you can select the saved version, and use Changes and the other buttons. You can of course save the new version to the repository too; once you Refresh the repository view, it should look like Figure 6.20.
To save the new package to a repository other than the package cache, you need to first make sure that Monticello knows about the repository, adding it if necessary. Then you can use the Copy in the package-cache repository browser, and select the repository to which the package should be copied. You can also associate the desired repository with the package by action-clicking on the repository and selecting add to package … , as shown in Figure 6.21. Once the package knows about a repository, you can save a new version by selecting the repository and the package in the Monticello Browser, and clicking Save . Of course, you must have permission to write to a repository. The PharoByExample repository on SqueakSource is world readable but not world writable, so if you try and save there, you will see an error message. However, you can create your own repository on SqueakSource by using the web interface at http://www.squeaksource.com, and use this to save your work. This is especially useful as a mechanism to share your code with friends, or if you use multiple computers.
If you do try and save to a repository where you don’t have write permission, a version will nevertheless be written to the package-cache. So you can recover by editing the repository information (action-click in the Monticello Browser) or choosing a different repository, and then using Copy from the package-cache browser.
One of the things that makes Smalltalk so different from many other programming environments is that it is provides you with a window onto a world of live objects, not a world of static code. Any of those objects can be examined by the programmer, and even changed — although some care is necessary when changing the basic objects that support the system. By all means experiment, but save your image first!
As an illustration of what you can do with an inspector, type TimeStamp now in a workspace, and then action-click and choose inspect it .
(It’s not necessary to select the text before using the menu; if no text is selected, the menu operations work on the whole of the current line. You can also type CMD–i for inspect it .)
A window like that shown in Figure 6.22 will appear. This is an inspector, and
can be thought of as a window onto the internals of a particular object — in this
case, the particular instance of TimeStamp
TimeStampthat was created when you evaluated the expression TimeStamp now. The title bar of the window shows the printable representation of the object that is being inspected. If you select self at the top of the left pane, the right pane will show the printstring of the object. The left pane shows a tree view of the object, with self at the root. Instance variables can be explored by expanding the triangles next to their names.
The horizontal pane at the bottom of the inspector is a small workspace window. It is useful because in this window, the pseudo-variable self is bound to the object that you have selected in the left pane. So, if you inspect it on
self - TimeStamp today
in the workspace pane, the result will be a Duration object that represents the time interval between midnight today and the instant at which you evaluated TimeStamp now and created the TimeStamp object that you are inspecting. You can also try evaluating TimeStamp now - self; this will tell you how long you have spent reading this section of this book!
In addition to self, all the instance variables of the object are in scope in the workspace pane, so you can use them in expressions or even assign to them. For example, if you select the root object in the left pane and evaluate jdn := jdn - 1 in the workspace pane, you will see that the value of the jdn instance variable will indeed change, and the value of TimeStamp now - self will increase by one day.
There are special variants of the inspector for Dictionaries, OrderedCollections, CompiledMethods and a few other classes that make it easier to examine the contents of these special objects.
The object explorer is conceptually similar to the inspector, but presents its information in a different way. To see the difference, we’ll explore the same object that we were just inspecting.
Select self in the inspector’s left-hand pane, then action-click and choose explore (I) .
The explorer window looks like Figure 6.23. If you click on the small triangle next to root, the view will change to Figure 6.24, which shows the instance variables of object that you are exploring. Click on the triangle next to offset, and you will see its instance variables. The explorer is really useful when you need to explore a complex hierarchic structure — hence the name.
The workspace pane of the object explorer works slightly differently from that of the inspector. self is not bound to the root object, but rather to the object that is currently selected; the instance variables of the selected object are also in scope.
To see the value of the explorer, let’s use it to explore a deeply-nested structure of objects.
Evaluate Object explore in a workspace.
This is the object that represents the class Object in Pharo. Note that you can navigate directly to the objects representing the method dictionary and even the compiles methods of this class (see Figure 6.25).
The debugger is arguably the most powerful tool in the Pharo tool suite. It is used not just for debugging, but also for writing new code. To demonstrate the debugger, let’s start by creating a bug!
Using the browser, add the following method to the class String:
"assumes that I’m a file name, and answers my suffix, the part after the last dot"
| dot dotPosition |
dot := FileDirectory dot.
dotPosition := (self size to: 1 by: -1) detect: [ :i | (self at: i) = dot ].
↑ self copyFrom: dotPosition to: self size
Of course, we are sure that such a trivial method will work, so instead of writing an SUnit test, we just type ’readme.txt’ suffix in a workspace and print it (p) . What a surprise! Instead of getting the expected answer ’txt’, a PreDebugWindow pops up, as shown in Figure 6.26.
The PreDebugWindow has a title-bar that tells us what error occurred, and shows us a stack trace of the messages that led up to the error. Starting from the bottom of the trace, UndefinedObject»DoIt represents the code that was compiled and run when we selected ’readme.txt’ suffix in the workspace and asked Pharo to print it . This code, of course, sent the message suffix to a ByteString object (’readme.txt’). This caused the inherited suffix method in class String to execute; all this information is encoded in the next line of the stack trace, ByteString(String)»suffix. Working up the stack, we can see that suffix sent detect:…and eventually detect:ifNone sent errorNotFound.
To find out why the dot was not found, we need the debugger itself, so click on Debug .
The debugger is shown in Figure 6.27; it looks intimidating at first, but it is quite easy to use. The title-bar and the top pane are very similar to those that we saw in the PreDebugWindow. However, the debugger combines the stack trace with a method browser, so when you select a line in the stack trace, the corresponding method is shown in the pane below. It’s important to realize that the execution that caused the error is still in your image, but in a suspended state. Each line of the stack trace represents a frame on the execution stack that contains all of the information necessary to continue the execution. This includes all of the objects involved in the computation, with their instance variables, and all of the temporary variables of the executing methods.
In Figure 6.27 we have selected the detect:ifNone: method in the top pane. The method body is displayed in the center pane; the blue highlight around the message value shows that the current method has sent the message value and is waiting for an answer.
The four panes at the bottom of the debugger are really two mini-inspectors (without workspace panes). The inspector on the left shows the current object, that is, the object named self in the center pane. As you select different stack frames, the identity of self may change, and so will the contents of the self-inspector. If you click on self in the bottom-left pane, you will see that self is the interval (10 to: 1 by -1), which is what we expect. The workspace panes are not needed in the debugger’s mini-inspectors because all of the variables are also in scope in the method pane; you should feel free to type or select expressions in this pane and evaluate them. You can always cancel (l) your changes using the menu or CMD–l.
The inspector on the right shows the temporary variables of the current context. In Figure 6.27, value was sent to the parameter exceptionBlock.
As we can see one method lower in the stack trace, the exceptionBlock is [self errorNotFound: ...], so, it is not surprising that we see the corresponding error message.
Incidentally, if you want to open a full inspector or explorer on one of the variables shown in the mini-inspectors, just double-click on the name of the variable, or select the name of the variable and action-click to choose inspect (i) or explore (I) . This can be useful if you want to watch how a variable changes while you execute other code.
Looking back at the method window, we see that we expected the penultimate line of the method to find dot in the string ’readme.txt’, and that execution should never have reached the final line. Pharo does not let us run an execution backwards, but it does let us start a method again, which works very well in code such as this that does not mutate objects, but instead creates new ones.
Click Restart , and you will see that the locus of execution returns to the first statement of the current method. The blue highlight shows that the next message to be sent will be do: (see Figure 6.28).
The Into and Over buttons give us two different ways to step through the execution. If you click Over , Pharo executes the current message-send (in this case the do:) in one step, unless there is an error. So Over will take us to the next message-send in the current method, which is value — this is exactly where we started, and not much help. What we need to do is to find out why the do: is not finding the character that we are looking for.
After clicking Over , click Restart to get back to the situation shown in Figure 6.28.
Click Into ; Pharo will go into the method corresponding to the highlighted message-send, in this case, Collection»do:.
However, it turns out that this is not much help either: we can be fairly confident that Collection»do: is not broken. The bug is much more likely to be in what we asked Pharo to do. Through is the appropriate button to use in this case: we want to ignore the details of the do: itself and focus on the execution of the argument block.
Select the detect:ifNone: method again and Restart to get back to the state shown in Figure 6.28. Now click on Through a few times. Select each in the context window as you do so. You should see each count down from 10 as the do: method executes.
When each is 7 we expect the ifTrue: block to be executed, but it isn’t. To see what is going wrong, go Into the execution of value: as illustrated in Figure 6.29.
After clicking Into , we find ourselves in the position shown in Figure 6.30. It looks at first that we have gone back to the suffix method, but this is because we are now executing the block that suffix provided as argument to detect:. If you select dot in the context inspector, you will see that its value is ’.’. And now you see why they are not equal: the seventh character of ’readme.txt’ is of course a Character, while dot is a String.
Now that we see the bug, the fix is obvious: we have to convert dot to a character before starting to search for it.
Change the code right in the debugger so that the assignment reads dot := FileDirectory dot first and accept the change.
Because we are executing code inside a block that is inside a detect:, several stack frames will have to be abandoned in order to make this change. Pharo asks us if this is what we want (see Figure 6.31), and, assuming that we click yes , will save (and compile) the new method.
The evaluation of the expression ’readme.txt’ suffix will complete, and print the answer ’.txt’.
Is the answer correct? Unfortunately, we can’t say for sure. Should the
suffix be .txt or txt? The method comment in suffix is not very precise. The
way to avoid this sort of problem is to write an SUnit test that defines the
self assert: ’readme.txt’ suffix = ’txt’
The effort required to do that was little more than to run the same test in the workspace, but using SUnit saves the test as executable documentation, and makes it easy for others to run. Moreover, if you add method 6.2 to the class StringTest and run that test suite with SUnit, you can very quickly get back to debugging the error. SUnit opens the debugger on the failing assertion, but you need only go back down the stack one frame, Restart the test and go Into the suffix method, and you can correct the error, as we are doing in Figure 6.32. It is then only a second of work to click on the Run Failures button in the SUnit Test Runner, and confirm that the test now passes.
Here is a better test:
self assert: ’readme.txt’ suffix = ’txt’.
self assert: ’read.me.txt’ suffix = ’txt’
Why is this test better? Because it tells the reader what the method should do if there is more than one dot in the target String.
There are a few other ways to get into the debugger in addition to catching
errors and assertion failures. If you execute code that goes into an infinite loop,
you can interrupt it and open a debugger on the computation by typing
CMD–. (that’s a full stop or a period, depending on where you learned
You can also just edit the suspect code to insert self halt. So, for example, we might
edit the suffix method to read as follows:
"assumes that I’m a file name, and answers my suffix, the part after the last dot"
| dot dotPosition |
dot := FileDirectory dot first.
dotPosition := (self size to: 1 by: -1) detect: [ :i | (self at: i) = dot ].
↑ self copyFrom: dotPosition to: self size
When we run this method, the execution of the self halt will bring up the pre-debugger, from where we can proceed, or go into the debugger and look at variables, step the computation, and edit the code.
That’s all there is to the debugger, but it’s not all there is to the suffix method.
The initial bug should have made you realize that if there is no dot in the target
string, the suffix method will raise an error. This isn’t the behaviour that we
want, so let’s add a second test to specify what should happen in this
self assert: ’readme’ suffix = ’’
Add method 6.5 to the test suite in class StringTest, and watch the test raise an error. Enter the debugger by selecting the erroneous test in SUnit, and edit the code so that the test passes. The easiest and clearest way to do this is to replace the detect: message by detect: ifNone:, where the second argument is a block that simply returns the string size.
We will learn more about SUnit in Chapter 7.
Smalltalk is a multi-threaded system: there are many lightweight processes (also known as threads) running concurrently in your image. In the future the Pharo virtual machine may take advantage of multiprocessors when they are available, but at present concurrency is implemented by time-slicing.
The process browser is a cousin of the debugger that lets you look at the various processes running inside Pharo. Figure 6.33 shows a screenshot. The top-left pane lists all of the processes in Pharo, in priority order, from the timer interrupt watcher at priority 80 to the idle process at priority 10. Of course, on a uniprocessor, the only process that can be running when you look is the UI process; all others will be waiting for some kind of event. By default, the display of processes is static; it can be updated by action-clicking and selecting turn on auto-update (a)
If you select a process in the top-left pane, its stack trace is displayed in the top-right pane, just as with the debugger. If you select a stack frame, the corresponding method is displayed in the bottom pane. The process browser is not equipped with mini-inspectors for self and thisContext, but action-clicking on the stack frames provide equivalent functionality.
There are two tools in Pharo to help you find messages. They differ in both interface and functionality.
The method finder was described at some length in Section 1.9; you can use it to find methods by name or by functionality. However, to look at the body of a method, the method finder opens a new browser. This can quickly become overwhelming.
The message names browser has more limited search functionality: you type a fragment of a message selector in the search box, and the browser lists all methods that contain that fragment in their names, as shown in Figure 6.34. However, it is a full-fledged browser: if you select one of the names in the left pane, all of the methods with that name are listed in the right pane, and can be browsed in the bottom pane. As with the browser, the message names browser has a button bar that can be used to open other browsers on the selected method or its class.
Whenever you are working in Pharo, any changes that you make to methods and classes are recorded in a change set. This includes creating new classes, re-naming classes, changing categories, adding methods to existing classes — just about everything of significance. However, arbitrary doits are not included, so if, for example, you create a new global variable by assigning to it in a workspace, the variable creation will not make it into a change set.
At any time, many change sets exist, but only one of them — ChangeSet current — is collecting the changes that are being made to the image. You can see which change set is current and can examine all of the change sets using the change sorter, available by selecting World ⊳ Tools … ⊳ Change Sorter .
Figure 6.35 shows this browser. The title bar shows which change set is current, and this change set is selected when the change sorter opens.
Other change sets can be selected in the top-left pane; the action-click menu allows you to make a different change set current, or to create a new change set. The next pane lists all of the classes affected by the selected change set (with their categories). Selecting one of the classes displays the names of those of its methods that are also in the change set (not all of the methods in the class) in the left central pane, and selecting a method name displays the method definition in the bottom pane. Note that the change sorter does not show you whether the creation of the class itself is part of the change set, although this information is stored in the object structure that is used to represent the change set.
The change sorter also lets you delete classes and methods from the change set using the action-click menu on the corresponding items.
The change sorter allows you to simultaneously view two change sets, one on the left hand side and the other on the right. This layout supports the change sorter’s main feature, which is the ability to move or copy changes from one change set to another, as shown by the action-click menu in Figure 6.35. It is also possible to copy individual methods from one side to the other.
You may be wondering why you should care about the composition of a change set. the answer is that change sets provide a simple mechanism for exporting code from Pharo to the file system, from where it can be imported into another Pharo image, or into another non-Pharo Smalltalk. Change set export is known as “filing-out”, and can be accomplished using the action-click menu on any change set, class or method in either browser. Repeated file outs create new versions of the file, but change sets are not a versioning tool like Monticello: they do not keep track of dependencies.
Before the advent of Monticello, change sets were the main means for exchanging code between smalltalkers. They have the advantage of simplicity (the file out is just a text file, although we don’t recommend that you try to edit them with a text editor), and a degree of portability.
The main drawback of change sets, compared to Monticello packages, is that they do not support the notion of dependencies. A filed-out change set is a set of actions that change any image into which it is loaded. To successfully load a change set requires that the image be in an appropriate state. For example, the change set might contain an action to add a method to a class; this can only be accomplished if the class is already defined in the image. Similarly, the change set might rename or re-categorize a class, which obviously will only work if the class is present in the image; methods may use instance variables that were declared when they were filed out, but which do not exist in the image into which they are imported. The problem is that change sets do not explicitly describe the conditions under which they can be filed in: the file in process just hopes for the best, usually resulting in a cryptic error message and a stack trace when things go wrong. Even if the file in works, one change set might silently undo a change made by another change set.
In contrast, Monticello packages represent code in a declarative fashion: they describe the state of the image should be in after they have been loaded. This permits Monticello to warn you about conflicts (when two packages require contradictory final states) and to offer to load a series of packages in dependency order.
In spite of these shortcomings, change sets still have their uses; in particular, you may find change sets on the Internet that you want to look at and perhaps use. So, having filed out a change set using the change sorter, we will now tell you how to file one in. This requires the use of another tool, the file list browser.
The file list browser is in fact a general-purpose tool for browsing the file system (and also FTP servers) from Pharo. You can open it from the World ⊳ Tools … ⊳ File Browser menu. What you see of course depends on the contents of your local file system, but a typical view is shown in Figure 6.36.
When you first open a file list browser it will be focussed on the current directory, that is, the one from which you started Pharo. The title bar shows the path to this directory. The larger pane on the left-hand side can be used to navigate the file system in the conventional way. When a directory is selected, the files that it contains (but not the directories) are displayed on the right. This list of files can be filtered by entering a Unix-style pattern in the small box at the top-left of the window. Initially, this pattern is *, which matches all file names, but you can type a different string there and accept it, changing the pattern. (Note that a * is implicitly prepended and appended to the pattern that you type.) The sort order of the files can be changed using the name , date and size buttons. The rest of the buttons depend on the name of the file selected in the browser. In Figure 6.36, the file name has the suffix .cs, so the browser assumes that it is a change set, and provides buttons to install it (which files it in to a new change set whose name is derived from the name of the file), to browse the changes in the file, to examine the code in the file, and to filein the code into the current change set. You might think that the conflicts button would tell you about changes in the change set that conflicted with existing code in the image, but it doesn’t. Instead it just checks for potential problems in the file that might indicate that the file cannot properly be loaded (such as the presence of linefeeds).
Because the choice of buttons to display depends on the file’s name, and not on its contents, sometimes the button that you want won’t be on the screen. However, the full set of options is always available from the action-click more … menu, so you can easily work around this problem.
The code button is perhaps the most useful for working with change sets; it opens a browser on the contents of the change set file; an example is shown in Figure 6.37. The file contents browser is similar to the browser except that it does not show categories, just classes, protocols and methods. For each class, the browser will tell you whether the class already exists in the system and whether it is defined in the file (but not whether the definitions are identical). It will show the methods in each class, and (as shown in Figure 6.37) will show you the differences between the current version and the version in the file. Contextual menu items in each of the top four panes will also let you file in the whole of the change set, or the corresponding class, protocol or method.
It is quite possible to crash Pharo: as an experimental system, Pharo lets you change anything, including things that are vital to make Pharo work!
To maliciously crash Pharo, try Object become: nil.
The good news is that you need never lose any work, even if you crash and go back to the last saved version of your image, which might be hours old. This is because all of the code that you executed is saved in the .changes file. All of it! This includes one liners that you evaluate in a workspace, as well as code that you add to a class while programming.
So here are the instructions on how to get your code back. There is no need to read this until you need it. However, when you do need it, you’ll find it here waiting for you.
In the worst case, you can use a text editor on the .changes file, but since it is many megabytes in size, this can be slow and is not recommended. Pharo offers you better ways.
Restart Pharo from the most recent snapshot, and select World ⊳ Tools … ⊳ Recover lost changes .
This will give you the opportunity to decide how far back in history you wish to browse. Normally, it’s sufficient to browse changes as far back as the last snapshot. (You can get much the same effect by editing ChangeList browseRecent: 2000 so that the number 2000 becomes something else, using trial and error.)
One you have a recent changes browser, showing, say, changes back as far as your last snapshot, you will have a list of everything that you have done to Pharo during that time. You can delete items from this list using the action-click menu. When you are satisfied, you can file-in what is left, thus incorporating the changes into your new image. It’s a good idea to start a new change set, using the ordinary change set browser, before you do the file in, so that all of your recovered code will be in a new change set. You can then file out this change set.
One useful thing to do in the recent changes browser is to remove doIts . Usually, you won’t want to file in (and thus re-execute) doIts. However, there is an exception. Creating a class shows up as a doIt . Before you can file in the methods for a class, the class must exist. So, if you have created any new classes, first file-in the class creation doIts, then remove doIts and file in the methods.
When I am finished with the recovery, I like to file out my new change set, quit Pharo without saving the image, restart, and make sure that the new change set files back in cleanly.
In order to develop effectively with Pharo, it is important to invest some effort into learning the tools available in the environment.