Vous êtes sur la page 1sur 50

How to Use SELF 2.

0
Ole Agesen Lars Bak Craig Chambers Bay-Wei Chang Urs Hlzle John Maloney Randall B. Smith David Ungar

Welcome to SELF 2.0. This manual, How to Use SELF 2.0, is designed to take you through the steps needed to install and start using SELF. It has three parts: Getting Started Exploring SELF Programming and Debugging We hope you enjoy using SELF. Good luck! The SELF Group

Copyright (c) 1992, Sun Microsystems, Inc. and Stanford University. All Rights Reserved. Sun Microsystems, Inc 2550 Garcia Avenue Mountain View, CA 94043 USA RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the government is subject to restrictions as set forth in subparagraph (c) (1) (ii) of the Rights in Technical Data and Computer Software Clause at DFARS 252.227-7013 (Oct. 1988) and FAR 52.227-19(c) (June 1987). SOFTWARE LICENSE: The software described in this manual may be used internally, modied, copied and distributed to third parties, provided each copy of the software contains both the copyright notice set forth above and the disclaimer below. DISCLAIMER: Sun Microsystems, Inc. makes no representations about the suitability of this software for any purpose. It is provided to you "AS IS", without express or implied warranties of any kind. Sun Microsystems, Inc. disclaims all implied warranties of merchantability, tness for a particular purpose and non-infringement of third party rights. Sun Microsystems, Inc.'s liability for claims relating to the software shall be limited to the amount, if any of the fees paid by you for the software. In no event will Sun Microsystems, Inc. be liable for any special, indirect, incidental, consequential or punitive damages in connection with or arising out of this license (including loss of prots, use, data, or other economic advantage), however it arises, whether for breach of warranty or in tort, even if Sun Microsystems, Inc. has been advised of the possibility of such damage.

How to Use SELF 2.0

Part I Getting Started

To get started, you must install the SELF system from the distribution les, congure it for your computer, learn how to start and stop the SELF virtual machine, and create a SELF snapshot.

How to Use SELF 2.0

How to Install SELF

1 How to Install SELF


The SELF system can be installed from either tape or ftp. Installation consists of three steps: getting the archive les, performing the site installation, and setting up the working environment for an individual user.

1.1 Getting the Archive Files


The easiest way to get the SELF system is to use anonymous ftp to retrieve the les from self.stanford.edu. Archive les for the two supported platforms, Sun-4 and Sun-3, reside in the directories /pub/Self-release-2.0/sun4 and /pub/Self-release-2.0/sun3 respectively. If you do not have access to the internet, a tape can be obtained by sending email to self-request@stanford.edu. There may be a small fee to cover handling and media costs. Archive le
README Install Self.tar.Z Optional.Snapshot.tar.Z

Contents Describes how to install the system (similar to this chapter). Shell script to perform the installation. Archive containing the basic SELF system: the SELF virtual machine and SELF source les describing the SELF world. Contains a snapshot of the SELF world after reading in medium.self. An equivalent snapshot can be generated from the SELF source les in Self.tar.Z. Contains source code for the SELF virtual machine, roughly 85,000 lines of C++. An installation of GNU g++ version 2.1 is required to compile this code. This le is only necessary if you want to study or change the virtual machine or use the glue (see section 25 of the Reference Manual).

Optional.VM.tar.Z

Create a temporary directory to hold the archive les and retrieve README, Install, and Self.tar.Z into this directory. The other three les, Optional.Snapshot.tar.Z, Optional.Glue.tar.Z, and Optional.VM.tar.Z are optional.

1.2 Site Installation


Once you have obtained the archive les you must execute the shell script Install to perform the site installation. Install prompts you for the directory where the SELF site installation should reside and then unpacks the archive les. The default installation directory is /usr/local/lib. Prior to running Install, you should be sure there is sufcient disk space. The disk space requirements for the unpacked archive les are: Self.tar.Z 4 Mb
3

How to Use SELF 2.0

How to Install SELF

Optional.Snapshot.tar.Z Optional.VM.tar.Z

4 Mb 2 Mb

Type in the following to execute the Install script.


% sh Install

The archive les can be deleted when the site installation is complete.

1.3 Running SelfUserSetup


Before using SELF, each user must execute the script SelfUserSetup to congure the system for his or her own use. SelfUserSetup will do the following: Check your machine configuration and calculate the optimal setup for your machine. If the script tells you to add swap space please read section 1.4. Add a set of environment variable definitions to your ~/.cshrc file, based on the result of the previous calculation. Make a local copy of the SELF source files if requested. Create the .selfrc file in your home directory. The environment variable denitions added to the .cshrc le will not take effect until the next time a shell is created. To apply these denitions to the current shell, execute the Unix command:
% source ~/.cshrc

Installation of SELF is now complete.

1.4 Adding Swap Space


If the SelfUserSetup script tells you to extend the size of the virtual memory additional swap space must be added to your system. Follow the instructions to add swap space. First check the size of your machines swap space:
% pstat -s 30640k allocated + 11944k reserved = 42584k used, 47552k available

The available number is the amount of swap space available for SELF. 1. Now, subtract that number from the amount of swap space that the congure script said you need for SELF. The difference is the amount of swap space you must add. 2. Find a disk with enough free space to hold the additional swap le. (You can use the Unix df command for this.) 3. Login as super user.

How to Use SELF 2.0

How to Start the SELF Virtual Machine

4. Create a swap le of the desired size:


# mkfile 12m /u1/mySwapFile

(The example creates a 12MB le named mySwapFile on the disk /u1. Any name may be chosen for the swap le. Remember to put this le on the disk you found in step 3.) 5. Edit /etc/fstab, adding the following line:
/mySwapFile swap swap rw,noquota 0 0

(Assuming that your swap le is named /mySwapFile.) 6. Make the new swap le available to the system:
# swapon -a

2 How to Start the SELF Virtual Machine


You are now ready to run SELF. This section describes what youll encounter in the bare SELF world; a later section will tell you how to create a default world of objects, a much more hospitable working environment. To start the SELF virtual machine, type Self at the Unix prompt:
% Self Self Virtual Machine Version 2.0, Apr 30 1992 08:50:12 Copyright 1989-92: The Self Group (type _Credits for credits) reading ~/.selfrc VM#

Upon entering SELF, you are greeted by the prompt VM#, indicating that the system is ready to accept input. You can now enter a SELF expression. This expression will be evaluated in the context of an object called the lobby, i.e., as if the expression were a method invoked with the lobby object as the receiver. SELF will evaluate the expression and print the result. The bare SELF world has only a very few predened objectsthe ones that are absolutely necessary for bootstrapping the system, like true and the prototypical string object (see section 23 of the Reference Manual). Thus, even basic methods like integer + are not dened:

The lobby objects special status as the receiver of messages evaluated in the root context makes it desirable to put the lobby in a central place in the SELF inheritance graph, and in fact, most objects in the default world inherit from the lobby. See the SELF World manual.

How to Use SELF 2.0

How to Run the Spy

VM# 3 + 4 A lookup error happened: 3 did not understand the message "+". The receiver also did not understand the lookup error message "undefinedSelector:Type:Delegatee:MethodHolder:Arguments:" which was subsequently sent to it. -- Self VM #0 (<error>:1): + = ( | _ self* = 3. _ :arg1 = 5. | "undefined selector error; this method automatically generated by the VM." ) #1 doIt = ( | _ self* = lobby. | 3 + 5 ) #2 (<predefined>:1): printIt = ( | _ self* = lobby. | _Print ) VM# doIt

Ignore the details of the error message for the moment; they will be described in a later section. You only need to understand that the + message is not dened at this point. However, there are numerous primitives dened by the system, such as _IntAdd: for integer addition:
VM# 3 _IntAdd: 4 7: ( | ^ parent* = <0>. | )

Normally, you will not work with the system in this crude, initial state; rather, youll use more comfortable worlds created by reading in scripts or snapshots. Later sections explain how to do these things.

3 How to Run the Spy


The SELF system contains a system monitor, or spy, which displays information about the internal workings of the system such as memory management and compilation. It can be turned on or off by evaluating _Spy: true or _Spy: false (the return value is the previous state of the option.) When the spy is active, it takes over the bottom portion of your screen with the following display:

VM activity

VM memory

object memory

code cache

The indicators in the left part of the display correspond to various internal activities and events. The bars on the right show how memory is being used. To understand how to interpret this display,

How to Use SELF 2.0

How to Interrupt Programs and Exit SELF

see Appendix G of the Reference Manual. The spy can be useful simply to reassure you that SELF is still running during the longish pauses that sometimes occur when a large method is compiled or when the garbage collector runs. We suggest that you start the spy now.
By default, the spy displays on /dev/fb, the console framebuffer on the computer on which the virtual machine is running. It writes directly to the framebuffer, bypassing any window system. If you run SELF on a remote computer (e.g., via rlogin), the system monitor will write to the screen of the remote machine, not your own. If your computer has several screens, you can use the _SpyFramebufferName: option to specify the framebuffer on which the system monitor should appear, for example, /dev/cgsix0.

4 How to Interrupt Programs and Exit SELF


Before going on, it is important to know how to exit from the virtual machine, interrupt a long computation, and stop and start the SELF process scheduler.

4.1 How to Exit the SELF Virtual Machine


Table 1 How to exit SELF If SELF prompts you with
anything or nothing VM# Self 17>

Type
Control-Z, then kill -9 % to the Unix shell Control-D or _Quit two Control-Ds or _Quit

Table 1 summarizes the various ways to exit the SELF virtual machine. The rst way forcefully exits the virtual machine no matter what it is currently doing and returns to the Unix shell.

4.2 How to Interrupt a SELF Program


There are two ways to interrupt a running SELF program, Control-C and Control-\. The second way works even if the SELF process scheduler is not running. In response to the interrupt, you will see one of two things. If the SELF scheduler is not running, you will be returned directly to the VM# prompt. If the scheduler is running, you will be presented with a list of SELF processes (the process menu):

How to Use SELF 2.0

How to Interrupt Programs and Exit SELF

Self 9> 100000 * 100000 do: [] ^C ----------------Interrupt----------------Ready: <25> scheduling process{ 100000 * 100000 do: [] } Waiting: <26> waiting process{ inputLoop } -----------------------------------------Select a process (or q to quit scheduler): 25 Select | [a]bort, | | [b]ackground, | | [s]uspend, | | [r]esume, | | [p]rint stack, | or | [n]o action | for process 25: a Process 25 aborted. -----------------------------------------Self 10>

In this example, the loop was interrupted by typing Control-C, and the process menu was used to abort the process. If the user had typed q to quit the scheduler, all current processes would have been aborted along with the scheduler itself:
... -----------------------------------------Select a process (or q to quit scheduler): q Scheduler shut down. -----------------------------------------prompt VM#

The scheduler has been stopped, returning the user to the VM# prompt. The command prompt start restarts the scheduler:
VM# prompt start Self 11>

Although the VM# prompt can be used to evaluate expressions directly, the scheduler supports much nicer error messages and debugging, so it is usually best to run the scheduler. (The scheduler is started automatically when the default world is created.)

How to Use SELF 2.0

How to Build and Save the World

4.3 Summary
Table 2 Interrupting and Exiting SELF Control-C
Interrupts current SELF process via SELF scheduler The current prompt is Self17> (the SELF scheduler is running) The current prompt is VM# (the SELF scheduler is not running) At prompt Running SELF code At prompt Running SELF code process menu process menu no effect no effect

Control-\
Interrupts current SELF process directly process menu process menu no effect returns to VM# prompt

Control-D
Signals end-of-le when reading standard input exits scheduler (goes to VM# prompt) no effect exits VM no effect

_Quit
VM quit primitive

exits VM no effect exits VM no effect

Certain virtual machine operations like garbage collection, reading a snapshot, and compilation cannot be interrupted; interrupts during these operations will be deferred until the operation is complete. As a last resort (e.g., if the system appears to be hung), you can force an abort by pressing Control-\ ve times in a row.

5 How to Build and Save the World


You probably already know that there are two parts to SELF: a virtual machine (VM) that executes SELF programs, and a world of SELF objects (including programs) that live in the memory of the virtual machine. The world of objects can be saved into and later restored from a special kind of le, called a snapshot. A snapshot captures the state of the object world: that is, all the objects and methods, but not the state of currently running processes. This section describes how to create a default object world by reading in the SELF source code provided with your distribution and how to save this world as a snapshot. (If you already have a snapshot, you can skip this section for now.)

5.1 Creating the World


To create the default object world: 1. Start SELF:
% Self Self Virtual Machine Version 2.0, Apr 30 1992 08:50:12 Copyright 1989-92: The Self Group (type _Credits for credits) reading ~/.selfrc VM#

2. Check the directory path that SELF will use to nd source les:

How to Use SELF 2.0

How to Build and Save the World

VM# _DirPath '~/self/self' <0>: ( | ^ parent* = <1>. | byte array: {126, 47, 115, 101, 108, 102, 47, 115, 101, 108, 102} )

The result string is a list of directories, separated by colons. Ignore the output starting with <0>:; this is just a different way of printing the string. A zero-length directory name is an abbreviation for the current directory. In particular, if _DirPath is the empty string, then the path includes just the current directory. Paths may also include the tilde character (~), which is expanded as it is in the Unix C-shell. 3. If the directory containing your source les is not in the path, then change the path:
VM# _DirPath: 'myDirectory'
You can define a default value for _DirPath by including a line like _DirPath: '.:/tmp:~smith/self' in a file named .selfrc in your login directory.

4. (Optional, but recommended.) If you are running on the console screen of your workstation, start the spy so you can watch the world ll up with objects:
VM# _Spy: true

5. Read in the default world. To do this, ask SELF to read expressions from the le all.self:
VM# 'all' _RunScript

Unless you have asked SELF not to print script names, you should see something like:
reading mydir/all... reading mydir/all-core... reading mydir/init... . . .

6. After all the les have been read in, SELF will start the process scheduler, initialize its cache of path names of well-known objects, and print:
refilling path cache...done. Self 0>

That last line is the SELF prompt indicating that the virtual machine is ready to read and evaluate expressions. Congratulations! You are now ready to explore the world of SELF objects. But, rst you should save the world.

5.2 Saving the World


To save the world, you need to write a snapshot le. This le consumes over 5Mb of disk space, so be sure to put it on a disk with enough free space. If the snapshot le name doesnt start with a slash, tilde, or dot, SELF will put it in the rst directory in your DirPath (see step 2 above). Often, you will just name the le Snapshot and put it in the current directory:

10

How to Use SELF 2.0

How to Build and Save the World

Self 1> './Snapshot' _WriteSnapshot './Snapshot' this string is the value returned by _WriteSnapshot Self 2>

The next time you start SELF, you can just read in this snapshot and start working; you will not need to rebuild the default world.

11

How to Use SELF 2.0

How to Build and Save the World

Part II Exploring SELF

Now that you have built yourself a default SELF world, you are ready to read in your snapshot and explore this world using either text-based interactions or the graphical object browser.

12

How to Use SELF 2.0

How to Read a Snapshot

6 How to Read a Snapshot


Start SELF if you are not already running it. Then, type the name of your snapshot le in single quotes followed by _ReadSnapshot.
VM# 'Snapshot' _ReadSnapshot Self 0>

When the Self 0> prompt appears, you are ready to type in SELF expressions and explore the SELF world. The snapshot le name can be a relative or absolute path name, and tildes will be expanded as they are in the Unix C-shell. If the rst component in the le name is not the root, tilde, or the current directory, then SELF will search the sequence of directories set by _DirPath: for a le of the given name.
As a shortcut, you can just type the name of the snapshot to the Unix shell as if it were a command; Unix will run whatever version of the SELF virtual machine it nds in your search path and then read the snapshot.

7 How to Use the .selfrc File


When SELF is rst started, the virtual machine looks for the le .selfrc in your login directory and, if it nds the le, evaluates all SELF expressions contained therein. This mechanism allows you to customize your SELF environment. For example, you can dene custom shortcuts for frequently typed expressions. The .selfrc le can also set virtual machine options such as _DirPath. Here is an annotated example:
_DirPath: '~/mySelfDirectory' _SnapshotCode: true shell _AddSlotsIfAbsent: ( | spyOn = ( _Spy: true ). spyOff = ( _Spy: false ). gc = ( _GarbageCollect ). quit = ( _Quit ). | )
set the VMs search path VM option to save compiled code with snapshot Command shortcuts added to the shell object: turn spy on turn spy off run garbage collector quit

The .selfrc le is executed immediately after SELF starts up. If you are starting up a snapshot, the .selfrc le is executed before the snapshot is read in. Thus, any command shortcuts or other changes to the SELF world made by the .selfrc le are overwritten when the snapshot is read in. However, virtual machine settings like _DirPath: and _SnapshotCode: survive.

8 How to Evaluate Expressions


Arbitrary SELF expressions can be typed at the SELF prompt. Each expression is evaluated and the result printed by the SELF read-eval-print loop. Here are some examples:
Self 0> 3 + 4 7
The system reads the expression, evaluates it, and prints the result.

13

How to Use SELF 2.0

How to Evaluate Expressions

Self 1> 20 factorial 2432902008176640000

Note that the system prints a prompt string such as Self 0> to indicate that it is ready to accept an expression. The number in the prompt string is an index into the command history (discussed in the next section). The value of the evaluated expression is always printed, even if the expression performs explicit output. For example, in the following example, the nal 10 printed is the value of the do: expression:
Self 2> 10 do: [| :i | (i * 2) print. print] 0 2 4 6 8 10 12 14 16 18 10

Multi-line expressions can also be entered. If the read-eval-print loop discovers that the expression typed so far has open (unbalanced) parentheses or brackets, it provides a continuation prompt (>>), allowing the expression to be continued on the next line. One can enclose a sequence of expressions in an extra set of parentheses to force the parser to read input lines until the final closing parenthesis. This handling of multiple line expressions is also used by the parser when evaluating expressions in script les (see section 16). For example:
self 3> 4 do: [| :i | >> (i printString, ! = , i factorial printString) >> printLine ] 0! = 1 1! = 1 2! = 2 3! = 6 4

Sometimes an input expression contains an error. For example:


Self 3> snort No snort slot found in <0> shell. ## Stack trace for process <50> ##___Rcvr__Selector/Block_________File:line______Method-holder... #5 <0> snort <error>:1 shell

The rst line of the error explains the cause of the error. In this case, the message snort was not understood by the implicit receiver shell (the shell object is discussed in the next section). Section 13 describes all the kinds of errors that can arise in SELF programs.

8.1 The shell object


In order for a SELF expression to be evaluated, the meaning of self must be dened. When an expression is evaluated by the read-eval-print loop, the shell object plays the role of self:
Self 5> self shell

The shell is a child of the lobby:

14

How to Use SELF 2.0

How to Evaluate Expressions

Self 6> shell parent lobby

The lobby is a common parent of many objects in the system and therefore provides a kind of linguistic common ground for the language. In particular, many useful objects such as true and all
parents of the lobby.

In the expressions you type in, a message sent to self gets sent to the shell object, and method lookup passes on from there through parent links.

lobby

shell

Many objects inherit from the lobby.

...
the prototype objects are accessible from the lobby. When the virtual machine evaluates the expressions you type, messages implicitly sent to self get looked up rst in the shell object, then in the lobby, then in the lobbys parents, and so on.
The actual details of the read-eval-print loop is as follows: an expression is read from the standard input. The input is treated as if it were dening the body of a method in the doIt slot of the lobby. If the input is syntactically correct, the system installs the generated bytecodes as the doIt method and creates a new process which sends printIt to the lobby. printIt is a SELF method (modiable by the user) which calls doIt and then prints the result.

Expressions read from a le by the _RunScript primitive, however, are handled slightly differently: they are evaluated in the context of the lobby itself, not the shell object. This arrangement keeps the lobby free of temporary slots added by the user, preventing accidental name clashes. The programmer can thus safely augment the shell object with slots to store temporary results and shortcuts to reduce typing (i.e., methods playing the role of macros). For example:
Self 7> _AddSlots: ( | s | ) shell Self 8> s: screenBitmap shell Self 9> s xorRect: 0@0 To: 30@30 <pixrect(live)>
Adds the slot s to the shell. Puts the screenBitmap into the s slot. Tell s to draw a rectangle on the screen.

Self 10> _AddSlots: ( | makeBox = ( s xorRect: 0@0 To: 30@30 ) | ) shell Self 11> makeBox shell
Shortcut to draw the rectangle s on the screen.

The shell has another parent in addition to the lobby: the enter parent. The enter slot normally points to an empty object. This slot is called enter because you can type enter: 3@4 and you

15

How to Use SELF 2.0

How to Use the Command History

expressions typed by the user will be lexically inside the point 3@4. That is, subsequent messages sent to self will be looked up in the point object because it is now a parent of the shell. Using the

Using multiple parent slots, the shell object inherits from both from the lobby and some other object. The shell slot that always points to the lobby is called parent. The other parent slot is called enter.

lobby

...
shell

enter slot can reduce typing when you nd an object you wish to understand better. For example:
Self 12> enter: 3@4 shell Self 13> x 3 Self 14> x: 7 shell Self 15> enter 7@4
A point object is made the shells enter parent. The x message finds a matching slot in the point. Change the value of x from within the object (x: is a private slot). Remember, enter is just the name of a slot in the shell.

The shells enter slot has higher priority than its parent slot. Thus, messages to self by the input expression will be looked up rst in the shell itself, then in the shells enter parent and other higher-priority parents, and then in the shells parent parent (a lower-priority parent, the lobby).

9 How to Use the Command History


You may notice that each time you type an expression to the SELF prompt, the number in the prompt string advances. That is because each command you type is recorded in a command history object, along with its result. The number in the prompt string is a history number that can later be used to refer to a given command in order to examine the result of the command or to redo the command. Only the most recent N entries are saved. The value of N can be changed with history keep: n.

16

How to Use SELF 2.0

How to Inspect and Print Objects

You can view the most recent entries by typing history printRecords. The resulting output will look something like this:
Self 5> history printRecords [0] 20 factorial [1] 3@4 [2] ui start [3] ui add: 3@4 [4] 17 parent

You can use the history object to get the result of a command or to redo it:
Self 6> history getResult: 4 traits smallInt Self 7> history execute: 0 2432902008176640000 Self 8> history getPrefix: '20 fac' [0] 20 factorial Self 9> history executePrefix: '17 par' traits smallInt

These are verbose expressions: you will probably want to dene shortcuts for the history operations you use most frequently. You can add these shortcuts to your .selfrc le so that they are automatically added to shell object each time you start SELF. Some suggested shortcuts for history operations are given in history.self.

10 How to Inspect and Print Objects


There are various ways to examine and print objects, several that depend on parts of the default world being functional and one that does not.

10.1 Using inspect:


The inspect: method attempts to print the contents of each slot in its argument. It also prints the denitions of methods contained in slots of the inspected object.
Self 0> inspect: 3@4 ( | _ parent* = traits point. _ thisObjectPrints = true. ^_ x <- 3. ^_ y <- 4. | ) 3@4 result printed by read-eval-print loop

The slot list is printed in normal SELF syntax. Asterisks following the slot name indicate parent slots. A private slot name is preceded by _ and a public slot by ^. A slot name followed by the symbol <- indicates that there is a corresponding assignment slot. In this example, the x and y slots have associated x: and y: slots. Note that the x and y slot names are preceded by the symbol

17

How to Use SELF 2.0

How to Inspect and Print Objects

^_, indicating that the accessing slots x and y are public but the corresponding assignment slots x: and y: are private. Here is a somewhat longer example:
Self 1> inspect: traits point ( | _ parent* = traits pair. ^ separator = @. ^ copyX: x Y: y = ( (clone x: x) y: y ). ^ xAxisReflect = ( copy y: y negate ). ^ yAxisReflect = ( copy x: x negate ). ^ restrictTo: rect = ( | xr <- nil. yr <- nil. | xr: x. yr: y. x < rect left ifTrue: [ xr: rect left ]. x > rect right ifTrue: [ xr: rect right ]. y < rect top ifTrue: [ yr: rect top ]. y > rect bottom ifTrue: [ yr: rect bottom ]. xr @ yr ). ^ alignToGrid: s = ( ((x / s width ) * s width ) @ ((y / s height) * s height) ). ^ # pt = ( rectangle from: self To: pt ). ^ ## sz = ( rectangle from: self To: + sz maxPoint ). | ) traits point result printed by read-eval-print loop

10.2 How objects are printed


This section briey discusses how objects are printed by the read-eval-print loop in the default world. For additional details, see the SELF Style Manual. Object-centric printing. Many objects in the default world understand messages that cause them to print themselves in an object-specic manner. The convention in the default world is for such methods to be invoked by sending print or printLine (which includes a newline after printing) to the object:
Self 17> (3 @ 4) printLine 3@4 This output is the result of sending the printLine message. 3@4 This is the result printed by the read-eval-print loop.

The message printString (and variations thereof) is used to get an objects printString without printing it:
Self 18> (3 @ 4) printString '3@4' The result is a string object, printed by the read-eval-print loop. Self 19> (3 @ 4) printString size 3 The printString is 3 characters long.

The read-eval-print loop. As previously mentioned, SELF sends printIt to the lobby when an expression is typed at the prompt, after rst installing the expression into the doIt slot in the shell. printIt attempts to print the result of doIt in an intelligent manner. It does this by invoking the
18

How to Use SELF 2.0

How to Inspect and Print Objects

name inferencer, a SELF-level mechanism that asks objects to print themselves if they know how, or tries to infer a name based on the objects location in the world otherwise. (This is not the case in the bare world, the minimal collection of objects you get when you start the SELF VM without reading in a snapshot or script les. In this case, printIt simply sends the _Print primitive, described in the next section, to the result of doIt.) The name inferencer uses the path cache, a SELF object that caches paths from the lobby to objects in the world. Objects that are reachable from the lobby (and thus appear in the path cache) are considered well-known. Here are some examples illustrating the name inferencer in the read-eval-print loop:
Self 1> 3 @ 4 3@4 Self 2> 17 parent traits smallInt
The name inferencer uses the printString method for points. The name inferencer named this object based on its location in the world of objects.

The name inferencer often generates a name that is actually a sequence of message sends that evaluates to the named object. For example:
Self 3> traits point traits point
This name is an expression you can evaluate to get the object.

The name traits point is a short version of the full path from the lobby to the traits for point objects, traits graphics point. The name for well-known objects that also respond intelligently to printString is a combination of the path name and the printString. For example, the prototype point object has both a location part and a name part:
Self 4> point <point>0@0
This name has both a location part (point) and a printString part (0@0).

In this case, the name part is short for prototypes graphics point.

10.3 Primitive Printing and Object Reference Numbers


The object printing and inspection mechanisms just discussed assume that certain basic facilities of the SELF world are working (e.g., string concatenation). This raises a bootstrapping question: what happens if these basic facilities themselves are broken and you need to debug them? The answer is to use the virtual machine primitives _Print and _AsObject. 10.3.1 Object Reference Numbers
_Print refers to objects by assigning them object reference numbers. An object reference number

is a small integer provided by the virtual machine as an object handle. One could use memory addresses
for this but addresses can be large numbers that are cumbersome to remember and type. Besides, the address of an object may change after a memory scavenge or garbage collection; in contrast, object reference numbers remain invariant. Object reference numbers are printed as an integer surrounded by angle brackets, for example

19

How to Use SELF 2.0

How to Inspect and Print Objects

<14>. The user can turn a reference number (an integer) into an object by sending it the _AsObject primitive. For example:
Self 5> 6 _AsObject 3@4
The object reference number 6 refers to the point 3@4.

The virtual machine only keeps track of the last n objects assigned reference numbers, where n is a configuration parameter controlled by the _NumObjectsIDs option (default: _NumObjectIDs: 1000). A given reference number is valid as long as it is within the last n reference numbers assigned. After this, it is forgotten and sending it the _AsObject primitive will fail. However, reference numbers are not recycled during a SELF session, so an invalid history number will not later be assigned to some other object; doing that could be very confusing!

10.3.2 The _Print Primitive The _Print primitive is a basic object printing function implemented entirely in the virtual machine. _Print prints its receiver and returns nil as the result. The output of the _Print primitive always lists at least the object reference number and slots of the receiver. It may also contain additional information for certain kinds of objects. The following sections describe the output of the _Print primitive in greater detail. Plain objects. Ordinary objects respond to _Print by printing their reference number and a list of their slots. _Print is a shallow operation: it does not recursively send _Print to the contents of its slots. Instead, the contents of a slot is printed as follows: its literal form if the slot contains an integer, a float, or a string, true, false, nil, or lobby if the slot contains one of these objects, <- if the slot contains the assignment primitive, <a method> if the slot contains an object that contains code, or the reference number of the referenced object if it is none of the above. The slot list is printed in normal SELF syntax. A private slot name is preceded by _ and a public slot by ^. Asterisks are appended to the slot name to indicate parent slots. For example, sending _Print to 3@4 gives:
Self 0> (3 @ 4) _Print <19>: ( | _ parent* = <18>. _ thisObjectPrints = true. ^ x = 3. _ x: = <-. ^ y = 4. _ y: = <-. | ) nil The result _Print is always nil.

The output shows that the object 3@4 has object reference number 19. Its parent slot contains an object with reference number 18. The slots thisObjectPrints, x, and y contain the boolean object true, and the integers 3 and 4 respectively. The slots x: and y: contain the assignment primitive. Here is another example:

20

How to Use SELF 2.0

How to Inspect and Print Objects

Self 1> (5 @ 7) _Print <20>: ( | _ parent* = <18>. _ thisObjectPrints = true. ^ x = 5. _ x: = <-. ^ y = 7. _ y: = <-. | ) nil

It can be seen that both 3@4 and 5@7 share the same parent (the point traits object) because both objects contain the object reference number 18 in their parent slots. To look at this object, we convert the reference number 18 into an object using the _AsObject primitive and print it:
Self 2> 18 _AsObject _Print <18>: ( | _ parent* = <21>. ^ separator = @. ^ copyX:Y: = <a method>. ^ xAxis Reflect = <a method>. ^ yAxisReflect = <a method>. ^ restrictTo: = <a method>. ^ alignToGrid: = <a method>. ^ # = <a method>. ^ ## = <a method>. | ) nil

Vectors. Object vectors and byte vectors display their array elements after their slots. The elements are printed in the same manner as the contents of slots. The _VectorPrintLimit option controls how many elements of a vector are printed. (The default is _VectorPrintLimit: 20.)
Self 3> someVector _Print <49>: ( | ^ parent* = <29>. | object array: {true, <50>, 2.45} ) nil

Integers and oats. Integers and oats are not assigned history numbers since their values can be directly printed and typed so easily.
Self 4> 1.3 _Print 1.3: ( | ^ parent* = <0>. | ) nil Self 5> 16r2f _Print 47: ( | ^ parent* = <1>. | ) nil
The output of _Print. The float 1.3 has one parent slot. A hexadecimal number (2F radix 16). Integers always _Print in decimal.

Strings, special objects, processes, and mirrors. Strings and the true, false, nil, and lobby objects print their conventional form before their reference number and slot lists. Process objects print the special keyword process before their reference number and slot lists. Mirror objects (see the SELF World manual) print the short form of their reectee in addition to the reference number and slots of the mirror.
Self 6> 'tree' _Print 'tree' <2>: ( | ^ parent* = <3>. | byte array: {116, 114, 101, 101} ) Strings are surrounded by single quotes. nil Self 7> true _Print true <4>: ( | _ parent* = <5>. ^ printString = true. ^ ifTrue:False: = <a method>. ^ || = <a method>. ^ && = <a method>. ^ ^^ = <a method>. ^ not = false. ^ asInteger = 1. | ) nil

21

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

Self 8> (reflect: 2@3) _Print Create a mirror on 2@3 and sends _Print to it. mirror <reflectee = <67>> <68>: ( | ^ parent* = <48>. _ thisObjec tPrints = true. | ) nil

11 How to Use the SELF Graphical User Interface


The SELF graphical user interface is a prototype browser for SELF. This prototype (hereafter referred to as the ui) serves as an inspector, allowing the user to explore the SELF object world by pointing and clicking. The ui currently does not support editing, and has only rudimentary object modication and message sending capabilities. It was developed to explore some ideas in user interfaces for object-oriented programming environments and is not intended to serve as a fulledged environment for SELF.

11.1 Requirements
The ui will work with either a monochrome or an 8-bit color framebuffer. However, a GX framebuffer is recommended for color operation; the ui may have poor performance on less powerful color frame buffers. The ui can run under OpenWindows (in certain congurations; see below) or on the console directly, with no window system running. In both versions, the ui uses Suns Pixrect graphics library, which draws directly into the framebuffer independent of any running window system. Therefore, the OpenWindows version of the ui has certain requirements beyond that of a normal X application. OpenWindows The preferred way to run the ui is under OpenWindows, Suns window system supporting X and NeWS. The window manager must be olwm or olvwm, the Open Look window manager that is standard with OpenWindows. A monochrome or color monitor may be used; however, for the color OpenWindows version the ui requires a GX framebuffer (or better). The OpenWindows version of the ui uses X-based primitives for opening the window and collecting user input events, but, as mentioned above, the graphics primitives in this version of SELF are based on Pixrect, not X, so the ui cannot run on remote window servers: it must be run on the local machine. Console The ui can also run outside any window system, on the console of the machine that SELF is running on. A monochrome or color framebuffer may be used. Normal printing to the console is not suppressed and may temporarily disturb the screen.

The Pixrect/X combination requires the use of certain window manager dependent code; in this case, the OpenWindows color version requires a hardware cursor, which is provided by OpenWindows, olwm, and the GX.

22

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

11.2 Conguring the UI


Two slots in the preferences miscellaneous object determine the physical screen upon which the ui runs: xDisplay (defaults to :0.0) and framebuffer (defaults to /dev/fb). For a multi-headed machine (having more than one screen), these slots can be set to other names as appropriate. After changing the contents of these slots you must evaluate the expression:
graphicsInit initialize

to make the prototypical screen object refer to the correct physical screen. The framebuffer and the X display must refer to the same physical screen so that the X window and the Pixrect graphics will be drawn together. If you start the ui and the X window appears on one screen and the graphics appear on another, check the contents of the xDisplay and framebuffer slots in preferences miscellaneous, and make sure you have sent the message graphicsInit initialize. When the ui starts it automatically congures itself to its environment. It will congure itself as either the OpenWindows version or the console version, as appropriateif the DISPLAY environment variable is dened, it will congure for OpenWindows; if it is not dened, it will congure for the console. It will also congure itself for monochrome or color operation, depending on the depth of the chosen framebuffer. It will use large fonts if the framebuffer is high-resolution. Finally, it will size itself to leave room for the spy if the spy is running on the same framebuffer.

11.3 Starting the UI


It is recommended that you start the spy (the SELF system monitor) before starting the ui. Start the spy by sending the message _Spy: true. The spy will let you know whether pauses are due to SELF code being executed, the system dynamically compiling SELF code or, perhaps, a garbage collection taking place. To start the ui, simply send the message ui start. This puts a single object into the ui, the lobby. To start the ui with a different object visible, use ui startWith: obj. In the OpenWindows version, you can resize the ui window, move it anywhere on the screen, or iconify it.

Because the ui is not a full-edged X application, it must rely on the X events it receives to determine these changes. Occasionally it will update its screen after the window has changed size or placement or has been iconied, but before it nds out about the change. In these cases, the Pixrect graphics primitives will draw over other windows in OpenWindows and you may need to do invoke the Refresh command from the OpenWindows desktop menu.

23

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

11.4 Summary of Mouse Button Functions


The ui primarily relies on the mouse for its operation, although it also uses several function keys. The following sections describe the operation of the ui in detail, but for starters, here is a short summary of the mouse button functions. Table 1 Mouse button functions in the ui Mouse button
left middle right

Function
grabbing (color changing) removing

Examples
sprouting contents, moving objects, popping up menu (color version only; shift key must be down to initiate) remove boxes from screen, hide slots, desprout arrows

11.5 Manipulating Objects in the UI


The ui presents SELF objects as three-dimensional boxes oating in their own articial reality. The slots of an object are depicted as wide slabs on the face of the object, and the contents of each slot are smaller slabs on the faces of the slots. The relationship between objects and the slots that contain (i.e., refer to) them is shown by arrows pointing from the slots to the objects. Objects can be moved about on the screen, the contents of their slots can be examined, and they can be removed from the screen. Moving objects. Objects can be picked up and moved about on the screen by pointing to the face of the object (or the face of one of its slots), pressing down and holding the left mouse button, and moving the mouse. Letting go of the left mouse button drops the object. Examining slots. Pressing down the left mouse button on the contents of a slot (the contents box) causes the object in the slot to display itself on the screen (sprout), connected to the slot by an arrow. Sprouting is simply a special case of grabbing; after sprouting an object, the object is in hand and can be moved immediately as long as the mouse button is held down. Navigating in the ui is performed by traversing slots to reach the desired object (the lobby object is a good starting point). Removing objects from the screen. Clicking the right mouse button on the body of an object (not one of its slots) removes that object from the screen. Hiding and showing slots. Clicking the right mouse button on a slot will hide that slot. Hidden slots do not show on the face of the object. When some of an objects slots are hidden, a long bar called the hidden-slots bar appears across its bottom. Pressing the left mouse button on the hiddenslots bar yields a pop-up menu that lists all the hidden slots. Selecting an item from this menu

24

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

makes the slot appear in the object. In addition to the hidden slots, this menu may include some of the special entries described in the table below. Table 2 Special items in the hidden slots menu Menu item
All Parents Public Indexable Well-known indexable Sendable

Effect
Shows all slots Shows only parent slots Shows only public slots Shows only the elements of the vector or byte vector part Shows only those elements of the vector part that are well-known (i.e., recorded in the path cache) Shows only send pseudo-slots

Except for the All item, which is always present in the hidden slots menu, special menu items appear only when there is at least one slot (or pseudo-slot) in that object matching that category. Hiding all slots (iconication). Clicking the right mouse button on the hidden-slots bar on the bottom of the box will hide all slots, effectively iconifying the object. If the object currently has no
hidden slots, you can make the hidden-slots bar appear by clicking on some slot with the right button to hide it, then right-click the hidden-slots bar to hide the rest of the slots.

Hiding links between objects. Clicking the right mouse button on a sprouted contents box simply removes the arrow between the slot and the object contained in the slot.

Menus do not reect the philosophy of concreteness we espouse for the ui (see [CU90]). They have been added to the ui as an expediency, to make this prototype more practical in the interim before a full-edged user interface is constructed.

25

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

Pseudo-slots. The ui permits access to certain parts of vectors, methods, and mirrors by way of pseudo-slots. For example, object vectors and byte vectors show their elements as pseudo-slots, in addition to showing their regular slots. Pseudo-slots have names enclosed in angle brackets. Table 3 Objects that have pseudo-slots in the ui Kind of object
process mirror vector byte vector method block generic activation method activation block activation process stack the object that the mirror reects (its reectee) vector elements (the element indices are the names of the pseudo-slots) byte vector elements (the element indices are the names of the pseudo-slots) byte code array, literal array, le name, le line number, source string lexical parent receiver, expression stack, byte code position, plus all method pseudo-slots selector, method holder, sender, plus all generic activation pseudo-slots selector, lexical parent, sender, plus all generic activation pseudo-slots

Pseudo-slots

Send pseudo-slots. Objects may appear in the ui with pseudo-slots that send a single unary message to that object. (See below for how to add objects with send pseudo-slots.) Clicking on the contents portion of a send pseudo-slot (labelled <<SEND>>) sends the message to the object. The object returned by the message send will appear in the ui.

26

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

Enumeration buttons. The ui can nd objects with certain relationships to a given object. These enumerations are requested by pressing an enumeration button with the left mouse button. There are ve kinds of enumerations, as depicted in the diagram below and explained in the table below. r - references

s - senders

i - implementors

m - messages c - children Table 4 Enumeration buttons Enumeration


references children senders implementors messages

Description
all references to this object all children of this object (a subset of all references to this object, in which the references are through parent slots) all senders of messages whose selector is the name of this slot all implementors of this message (the name of the slot) all messages sent in this method (appears only on method slots)

Enumeration results appear in the ui as vectors with only the interesting elements showing. If there are no objects satisfying the enumeration, an object appears saying so. Changing the contents of objects by grabbing arrows. [This is potentially a hazardous operation.] By setting allowArrowGrabbing to true in the preferences object (see below), the ui will allow the contents of a slot to be changed by grabbing the head of its outgoing arrow with the left mouse button and dropping it over another object. When the arrowhead is grabbed, it changes into a square, indicating that it is waiting for a target. Dropping the square onto another object makes the arrow to point to that object, thereby changing the contents of the slot that the arrow is coming from. (Be sure to drop the square onto the target object, not merely near it.) Some arrow redirections are not currently permitted by the ui. These include moving an arrow for a pseudo-slot and moving an arrow to a method object that takes a different number of arguments than the original contents of the slot. In these cases, a warning is printed in the SELF shell and no change is made. Dropping an arrow over empty space aborts the operation.
27

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

Be careful! Indiscriminately changing objects on which the rest of the system depends can break parts of the system, and redirecting arrows in the ui is tantamount to altering the running system. If the ui and other parts of the SELF system begin behaving strangely (many errors and stack dumps), it may be because a crucial part of the system was changed unintentionally. In this case, quit SELF and start anew. Function key operations (OpenWindows version only). Objects in the ui and the ui itself also respond to certain key press events from the keyboard. The relevant object is the object under the cursor at the time of the key press. If the cursor is on the background (i.e., not on any object), the key press generates a message to the ui itself. Table 5 Function key operations Key
Stop (L1) Props (L3) Cut (L10) Copy (L6) Paste (L8) Find (L9) On the background, quits the ui. Adds a mirror on the object into the ui. Removes the object from the ui, but retains a reference to the object in a hidden clipboard. Puts a reference to the object into the clipboard. On the background, puts the object in the clipboard into the ui. On an object, adds the slots of the object in the clipboard to the given object. sends enter: thisObject to the shell object. This means that the given object will be made a parent of the type-in shell. This allows you to evaluate expressions that are interpreted as if they are inside the given object. Adds to the ui the display object to which you are pointing. For example, if you point to the senders button on an object and hit F1, the sendersBox object that implements the button will appear in the ui. Finds the nontrivial parents of an object. Point to an object and press F2 to nd any parents of the object which themselves have parents. Traces the ancestry of an object. Point to an object, (move it to the lower lefthand corner of the screen), and press F3 to see all the objects ancestors which themselves contain parent slots. (Ancestors without parent slots are ignored in order to avoid cluttering things up with mere category objects.) F3 stops at the lobby, but you can go on by pointing to the lobby and pressing F3.

Effect

F1

F2 F3

It is fairly easy to extend this functionality: the ui sends keyPress: keyCode to the object under the cursor. User interface objects inherit keyPress: methods. The keyPress methods are in traits screenBox, traits baseBox, and in their common parent, traits nestedBox. (See the corresponding .self les.)

11.6 Manipulating Objects from the Prompt


Operations in the ui can be carried out from the prompt by sending messages to the ui object. The most useful of these messages is add:; the expression:
ui add: obj

28

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

makes the given object obj appear in the ui. To add the object to the ui with a send pseudo-slot for the unary message foo, use:
ui add: obj With: foo

The ui also responds to add:With:With:, add:With:With:With:, and add:WithMessages: (which takes a list of strings) to add the object with multiple send pseudo-slots. Nearly all the operations that can be carried out with the mouse in the ui can also be achieved by sending messages to ui. For details, see clientRequests in the le ui.self.

11.7 Customizing the UI


There are a number of ways to customize the way objects look and behave in the ui. Animation. The monochrome version of the ui has no animation beyond that of grabbing and moving the boxes. The color version of the ui, by default, does have animation (sprouting boxes, entering and exiting boxes, arrow sprouting, menu sprouting). To turn off this animation in the color version, send the message animator beDummy. To turn it back on, send the message animator beReal. OpenWindows icon. If the ui nds a le with the name specied in preferences (see below; by default, a le in the current directory named ui.icon), it will use that le for its icon when it is iconied. The icon le is an X bitmap format le. If no icon le is found, the ui uses the default OpenWindows icon. Colors. If the ui nds a le with the name specied in preferences (see below; by default, a le in the current directory named ui.colors), it will use that le for the initial color scheme. To create a color le, see section 12.8. Preferences. The ui uses the preferences object to control various options. To change a preference, set the appropriate slot of the preferences object to the desired value. For example, to enable

29

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

arrow grabbing, evaluate preferences allowArrowGrabbing: true. The ui preference slots are summarized in the following table: Table 6 UI preferences Slot Name
allowArrowGrabbing blurArrows blurBoxes colorFile grabAfterSprout iconFile useBigFonts

Description
If true, allow arrowheads to be grabbed and dropped on other objects. Color version only. If true, enable experimental motion blur for arrows. Color version only. If true, enable experimental motion blur for boxes. Color version only. File name to use for initial color scheme. Color version only. If true, hold on to the box after it has been sprouted if the mouse button is still down. File name to use for the ui icon. File should be in X bitmap format. OpenWindows version only. If true, use a bigger font and format for boxes. (The ui will always use big fonts for a high-resolution framebuffer.)

Default
false false true ui.colors true ui.icon false

Box blueprints. The ui has a facility to customize the way an object rst appears on the screen which slots are showing and what send pseudo-slots are shown. It does this by consulting a dictionary of blueprints for objects, keyed on the structure of the object (the names of all its slots). If a blueprint is found, the ui uses that to determine which slots are shown and which are hidden, and what send pseudo-slots to add to the object. If no blueprint is found, a default policy is used. You can add your own blueprints to this dictionary; see the le boxBlueprint.self.

11.8 Changing Colors


On a system with a color framebuffer, pressing down and holding the middle mouse button while simultaneously holding down the shift key causes the cursor to enter color space. Once color space
has been entered, the shift key may be released, but the middle button must remain pressed during the entire color changing operation.

Color space allows the colors of the ui to be changed interactively. Whatever the cursor is pointing at when color space is entered is whats color will be changed. (This includes, for example, the background, the boxes, the text on the boxes, and the arrows. In the case of arrows, point to the round tail of the arrow to change its color.) Entering color space transports the cursor to the absolute location of the current color in the ui window. In color space, the horizontal axis measures hue and the vertical axis measures brightness. Pressing the left button down in while in color space (while keeping the middle button down) switches the vertical axis from brightness to saturation.

In the console version of the ui, it is not necessary to hold down the shift key to enter color space.

30

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

Moving the cursor in color space interactively changes the color of the target elements. Releasing the middle button leaves color space. The color scheme for the ui is automatically saved after color changing. A le is created to hold the color information. This les name is dened by the preferences object (the default is ui.colors). The ui checks for this le when it starts up and, if it nds it, uses the given colors. Otherwise, it uses the default colors.

11.9 Leaving the UI


The expression ui quit always aborts the ui. Under OpenWindows, you can also leave the ui by going to the border of the X window, popping up the Window menu, and selecting Quit; or by pressing function key L1 (labelled Stop on many keyboards).

31

How to Use SELF 2.0

How to Use the SELF Graphical User Interface

Part III Programming and Debugging

Here is the heart of the matter: writing and debugging your own SELF programs.

32

How to Use SELF 2.0

How to Debug SELF Programs

12 How to Debug SELF Programs


The default world contains a tty-based debugger. The debugger allows the user to control the execution of a process and to examine the process stack. As described in [HCU92], the SELF system provides full source-level debugging with dynamic deoptimization. Debugging optimized code is therefore completely transparent to the user. Limited undocumented debugging facilities are also provided by the graphical user interface; see process.self. This section assumes that the SELF process scheduler is running. If the current prompt is VM#, you can type prompt start to start the scheduler:
VM# prompt start Self 1>

12.1 Examining an Error


When an error occurs, the scheduler suspends the active process and prints out an appropriate error message. (A complete list of possible error messages is given in section 14.) If the error message is too vague to discover the bug, you can attach the erroneous process to the debugger and explore the stack.
A new process is created for each expression evaluated by the read-eval-print loop. When an error occurs, the process is suspended and the debugger can be used to explore its stack.

Suppose you typed the following erroneous expression at the prompt:


Self 1> | i <- 0 | 10 do: [ i: i succ ] i print No i slot found in <5> <a child of traits block>. Sending method holder is <0> shell.

The error message alone many not be sufcient to nd the bug. To explore further, you can attach the process to the debugger:
Self 2> attach attaches the process that most recently encountered an error No i slot found in <5> <a child of traits block>. Sending method holder is <0> shell. shell <0> doIt = (| i <- 0. | 10 do: [ i: i succ ] i print) Process <6>, suspended with error, activation: 6 in [0,6]. File: <the prompt> shell

Attaching a process with an error sets the debuggers current activation to the bottommost interesting activation. In this case, the current activation is number 6. Activations 0 to 5 are omitted by the debugger since they are part of the error handling mechanism itself. By issuing the commands up and down the current activation can be changed to expose other stack activations. The current byte code position of the activation is highlighted by underlining part of the source code. The highlighting shows the message being be sent when the error occurred. Both the receiver and the selector are underlined. If your terminal does not support underlining, highlighting is shown by surrounding the expression with curly brackets ({}).
33

How to Use SELF 2.0

How to Debug SELF Programs

In this example, the highlighting shows that the message i is being sent to a block object. This explains the problem: the expression needed a period after the block to separate the expression into two statements.

12.2 Setting Breakpoints


Breakpoints can be added to a program by inserting halt statements (assuming that the receiver inherits from defaultBehavior). When a halt statement is executed, the currently executing process is suspended with a message indicating that a halt has occurred. The user can then examine the stack and control the execution of that process. Lets single step a typed in expression by setting a breakpoint as the rst statement:
Self 3> halt. 6 printLine Halt: running process{ halt. 6 printLine} <7>
attach to the process with history number 7 Self 4> attach: 7 shell <1> doIt = (halt. 6 printLine) the current byte code position is halt Process <7>, suspended, activation: 5 in [0,5]. File: <the prompt> shell

Self 5> step single step by one byte code shell <1> doIt = (halt. 6 printLine) Process <7>, suspended, activation: 5 in [0,5]. File: <the prompt> shell ... etc ...

As shown, breakpoints are explicit inserted in the source code. Setting a breakpoint in a method dened in a script le involves editing the script le and then reading it in. We hope to eliminate this inconvenience when a better programming environment is available. Caution: inserting halt in a method used by the core system can result in unexpected behavior (e.g., the debugger itself might break). This problem can be solved by using conditional breakpoints to restrict breakpoints to specic processes. The method break is an example of a conditional breakpoint that breaks the process only if the process is currently attached to a debugger.
break = (process this isDebugged ifTrue: [ halt ])

12.3 Interrupting a Running Process


A runaway process can be suspended (by typing control-C) and then attached to the debugger. Lets execute an innite loop where the value of slot i is incremented in each iteration:
Self 11> | i <- 0 | [ i: i succ ] loop

Typing control-C interrupts the scheduler and presents a list of all processes:

34

How to Use SELF 2.0

How to Debug SELF Programs

^C ----------------Interrupt----------------Ready: <0> scheduling process{ | i <- 0| [ i: i succ ] loop } Waiting: <1> waiting process{ inputLoop } -----------------------------------------Select a process (or q to quit scheduler): 0 select the loop process Select | [a]bort, | | [b]ackground, | | [s]uspend, | | [r]esume, | | [p]rint stack, | or | [n]o action | for process 0: s Process 0 suspended. ------------------------------------------

The runaway process is now suspended and can be attached to the debugger:
Self 12> attach: 0 <a child of t... <9> loop = ( value. _Restart. ) Process <0>, suspended, activation: 0 in [0,1]. File: ~/self/self/block:16 shell Self 13> up shell <10> doIt = (| i <- 1630863. | [ i: i succ ] loop ) Process <7>, suspended, activation: 1 in [0,1]. File: <the prompt> shell

12.4 The Well-Known printLine Technique


A simple and yet powerful way to monitor the program execution is to insert print statements in the methods you wish to observe. Since reading in a script le is fast in the SELF system, this technique is often the fastest way to isolate bugs in your program.

12.5 Tracing Message Sends


The system contains a rudimentary mechanism to trace the execution of a SELF program. This tracing facility is not commonly used, however, because in general it generates too much information to be useful (it was developed primarily for virtual machine debugging). It is described here for those rare occasions when such raw information is needed. Tracing prints a line for every non-inlined, non-inline-cached message send (that is, for every message send that actually requires a lookup at run-time). Tracing is activated and deactivated using the debugging primitive _Trace:. Because tracing generates a lot of output, you may want to insert _Trace: true and _Trace: false statements only around the relevant portions of your program.

35

How to Use SELF 2.0

How to Debug SELF Programs

To see more message sends, turn off inline caching during tracing. Send _InlineCache: false to turn it off, followed by _FlushInlineCache to ush all entries that have already been cached. To see every message send, turn off inlining with _Inline: false followed by _Flush to ush the code cache, eliminating previously inlined code. This will generate a lot of output! Caution: _Trace is not very useful when the scheduler is running (the usual case) since tracing is not restricted to a single SELF process. Tracing when the scheduler is running results in mixed trace output from all currently executing SELF processes.

12.6 Finding Objects


The browse object implements an enumeration facility that allows you to nd all the objects in the system with specic properties. The result of any browsing operation is a vector of mirrors. Example: nd all objects having the selector upLex:
Self 15> browse implementorsOf: upLex vector{mirror on (traits debugger inspecting)}

Example: nd all methods sending the message debugger:


Self 16> browse sendersOf: debugger vector{mirror on (connectDebugger = ( enter: debugger copy ))}

Example: nd all objects referring to the object shortcuts:.


Self 17> browse referencesTo: shortcuts vector{..., mirror on (shell)}

Example: nd all the objects in the system:


Self 18> browse all ...
result intentionally omitted to save paper!

12.7 Summary of Debugger Commands


The following table summarizes the debugger commands. Command
attach attach: n detach step next

Description
attach the process that most recently generated an error attach the process with object reference number n detach the debugged process single step one byte code step to next byte code in the current activation

See the VM Reference Manual.

36

How to Use SELF 2.0

How to Interpret Error Messages

Command
nish cont trace show show: n status up[: n] upLex down[: n] lookup: <name>

Description
nish executing the current activation continue execution print out a stack trace of the process display the current activation go to and display the nth activation on the stack display the status of the debugged process go up (n) activation(s) go up to the lexical enclosing scope of this activation go down (n) activation(s) lookup the given name in the context of the current activation

13 How to Interpret Error Messages


Five kinds of errors can occur during the execution of a SELF program: lookup errors, primitive errors, programmer dened errors, non-recoverable errors, and fatal VM errors. This section describes the various errors in each category. For each category, the general layout of error messages in that category will be explained along with the format of the stack trace. Then a rogues gallery of the errors in that category will be shown. By default, errors are handled by a set of methods dened in errorHandling.self. For all errors except non-recoverable and fatal VM errors, an object can handle errors in its own way by dening its own error handling methods. If the object in which an error occurs neither inherits nor denes error handling behavior, the VM prints out a low-level error message and a stack trace. The system will also resort to this low-level message and trace if an error is encountered while trying to handle an error.

13.1 An Example
Here is an expression that produces an error in the current system:
Self 7> 100000 factorial The stack has grown too big. (Self 2.0 limits stack sizes, and cannot resume processes with stack overflows.) ## Stack trace for process <9> ##___Rcvr__Selector/Block__________File:line______Method-holder/location____ #0 <10> asSmallInteger smallInt:132 traits smallInt coercions #1 <10> <= smallInt:86 traits smallInt comparing #2 <10> factorial integer:32 traits integer functions { #3 <11> [* predecessor fact...integer:32 [] in factorial #4 <12> ifTrue:False: boolean:47 false #5 <13> factorial integer:32 traits integer functions

37

How to Use SELF 2.0

How to Interpret Error Messages

}*309 #930 <0>

doIt

<the prompt>:1 shell

The error arose because the recursive method factorial exceeded the size allocated for the process stack which resulted in a stack overow. The virtual machine currently allocates a fixed-size stack to each process and does not extend the stack on demand.

The rst two lines of the output describe the error. These lines are followed by a stack trace of the erroneous process. Each line in the stack trace denotes a method or block activation and is divided into several elds: #0 the number of the stack frame. #0 is the most recent frame, i.e. the one that caused the stack overflow. The number of the last frame (#930) indicates the stack depth at the time of the error. the receiver of the message in this frame has object reference number 10. You can type inspect: 10 _AsObject to examine the receiver object.

<10>

asSmallInteger the methods selector name. If the frame is a block activation frame, the source code is shown in this field (as shown in frame #3 of this example). smallInt:132 the name and line number of the source file where this method is defined. traits smallInt coercions the methodholder of the method being executed. If the frame is a block activation, this is the frame method in which the block is defined (e.g., the block in frame #3 is defined in the method factorial). A sequence of recursive calls is presented as a folded structure. In the above example the notation
{ ... }* 309 represent 309 occurrences of a sequence of activations with the same structure as

activations #3, #4, and #5. One can explore the stack in greater detail using the debugger, as discussed in section 12.

13.2 Lookup Errors


Lookup errors occur when an object does not understand a message that is send to it. How the actual message lookup is done is described in the Language Reference Manual.
No foo slot found in shell <0>.

The lookup found no slot matching the selector foo.


More than one system slot was found in shell <0>. The matching slots are: oddballs <6> and prototypes <7>. The lookup found two matching system slots which means the message is ambig-

uous. The error message also says where the matching slots were found. Ambiguities can often be solved by changing parent priorities. No fish delegatee slot was found in <a child of lobby> <12>. The lookup found no parent slot fish, which was explicitly specified as the delegatee of the message.

38

How to Use SELF 2.0

How to Interpret Error Messages

No public c slot was found in <a child of lobby> <15>. Sending method holder is traits processErrors noPublicSelector <16>. Private slots were found in: <an object> <17> and <an object> <18>. The lookup found no public slot c but found two private slots. Changing a private

slot to a public slot will solve the problem.

13.3 Programmer Dened Errors


These errors are explicitly raised in the SELF program to report errors, e.g. sending the message first to an empty list will cause such an error.
Error: first is absent. Receiver is: list <7>.

Use the selectors error: and error:Arguments: to raise a programmer dened error.

13.4 Primitive Errors


Primitive failures occur when a primitive cannot perform the requested operation, for example, because of a missing or invalid argument. Appendix H of the Reference Manual describes how primitive failures are handled.
badTypeError: the _IntAdd: primitive failed. Its receiver was shell <6>. The primitive failed with badTypeError. The selector 12 could not be sent to shell because it is not a string. The primitive _Perform expects a string as its first argument. The selector add: could not be sent to shell <0> because it does not take 2 arguments. The primitive _Perform received the wrong number of arguments.

There are many other kinds of possible primitive errors (see Appendix H of the Reference Manual).

13.5 Non-Recoverable Process Errors


Errors that hinder the process to continue its execution are referred to as non-recoverable errors.
The stack has grown too big. (Self 2.0 limits stack sizes, and cannot resume processes with stack overflows.) A stack overflow error occurs because the current version of SELF allocates a

fixed size stack for each process, and the stack cannot be expanded.
Self 2.0 cannot run a block after its enclosing method has returned. (Self cannot resume this process, either.)

This error occurs if a block is executed after its lexically enclosing method has returned. This is call a non-LIFO block. Non-LIFO blocks are not supported by the current version of SELF.
39

How to Use SELF 2.0

How to Manipulate SELF Objects

13.6 Fatal Errors


In rare cases, the virtual machine may encounter a fatal error (e.g., a resource limit is exceeded or an internal error is discovered). When this happens, a short menu is displayed:
Internal error: signal 11 code 3 addr 4 pc 0x1ac768. Do you want to: 1) Quit Self 2) Try to print the Self stack 3) Try to return to the Self prompt 4) Force a core dump 5) Loop forever Your choice:

The rst line helps the SELF implementors locate the problem. Printing the SELF stack may provide more information about the problem but does not always work. Returning to the SELF prompt is usually successful, but the system integrity may have been compromised as a result of the error. Execute the primitive _Verify to verify the virtual machines integrity; see Table 22 in Appendix H of the Reference Manual). Since fatal errors usually arise from a bug in the virtual machine, please send the SELF group a bug report as described in section I-2.5, and include a copy of the error message if possible. If the error is reproducible please describe how to reproduce it.

14 How to Manipulate SELF Objects


This section explains how to change objects by adding, redening, and removing slots. The fundamental functionality for altering objects at this level is made available through mirrors. A mirror on a SELF object provides structural reection facilities for that object (see section 16 of the Reference Manual). In particular, mirrors implement methods for adding and removing slots to their object (reectee). The two basic methods are addSlots: and removeSlot:. All programming tasks in SELF can be built on top of these two methods. If you were to build a program manipulation facility, you would implement it using mirrors. However, when the system starts from a bare world, mirrors do not yet exist, so they cannot be used to build up the initial world of objects. Instead, the underlying primitives are used to directly modify objects. Actually, after the script defining mirrors has been processed, we could use mirrors the define and
add slots to objects in all subsequent scripts. This is currently not done simply because it is somewhat faster to call the primitives directly. The primitives corresponding to the two mirror methods previously mentioned

are:
_AddSlots: and _RemoveSlot:

Two other primitives are also provided:


_AddSlotsIfAbsent: and _Define:.

40

How to Use SELF 2.0

How to Manipulate SELF Objects

The current implementation does not allow integers, floats, canonical strings, or blocks as the receiver of or argument to the programming primitives. Of course, their parent objects may be used.

14.1 Adding Slots


Both the addSlots: method of mirrors and the _AddSlots: primitive add all the slots of the argument object (typically an object literal) to the receiver. If a slot already exists, _AddSlots: just assigns it a new value. The contents of read-only slots can be changed this way. Slots in the receiver but not in the argument object are left alone. Here are some examples:
Self 0> (reflect: shell) addSlots: ( | x = ( | | ) | )
Ask a mirror to add a slot called x to the shell whose contents is an empty object.

mirror on (shell) Self 1> inspect: x ( | | ) <an object> Self 2> >> >> >> >> >> >> >>
Use the _AddSlots: primitive to add... x _AddSlots: ( | a <- 17.8. An assignable slot initialized to 17.8. b. An assignable slot initialized to nil. c = 3. A constant slot. h = ( history printRecords ). A method slot. q = ( | a. b <- 'foo' | ). An object with its own slots. ^_ p <- nil. A public data slot with private assignment slot | ) (all other slots in this example are undeclared and have public semantics).

<an object> Self 3> inspect: x ( | a <- 17.8. b <- nil. c = 3. h = ( history printRecords ). q = <an object>. ^_ p <- nil. | ) <an object>

There must be periods between slot denitions. For example, if the period after slot h is omitted, SELF will send the q message to the parenthetical expression ( history printRecords ), resulting in an undened selector error. When a slot is redened, its contents are replaced with a reference to the new object, but other references to the original object are left unchanged. If the old slot was assignable and the new slot isnt, the assignment slot is removed:
Self 4> x _AddSlots: ( | a = 'new a' | )A change to x from previous example. <an object>

41

How to Use SELF 2.0

How to Manipulate SELF Objects

Self 5> x _Print <37>: ( | b = nil. c = 3. h = <a method>. q = <38>. ^ p = nil. b: = <-. _ p: = <-. a = 'new a'. | ) The a: slot is gone. nil
Note that expressions used to construct an object literal are always evaluated in the context of either the lobby (in the case of reading in a script le) or the shell object (in the case of evaluating an expression typed by the user), not in the context of the object being constructed.

14.2 Removing Slots


Both the removeSlot: method of mirrors and the _RemoveSlot: primitive remove a single slot from the receiver. The argument species the name of the slot to remove. If the slot being removed has a corresponding assignment slot, it is removed as well. For example:
Self 6> _AddSlots: ( | p = 3@4 | ) <an object> Self 7> inspect: p ( | _ parent* = traits point. _ thisObjectPrints = true. ^_ x <- 3. ^_ y <- 4. | ) 3@4 Self 8> p _RemoveSlot: y: Self 11> inspect: p ( | _ parent* = traits point. _ thisObjectPrints = true. ^_ x <- 3. ^ y = 4. | ) 3@4
Create a point named p. Inspect p.

Remove ps y: slot.

Note that all the object manipulation primitives in SELF always manipulate individual objects the above example did not remove the y: slot from all points, only from the point p. Of course, if traits point, the object containing the shared behavior for all points, were modied then all points would feel the change via inheritance, even though only a single object was changed.

14.3 The _Define: Primitive


_AddSlots: and _RemoveSlot: change some of the slots in an object without effecting any oth-

er slots of the object. These primitives incrementally modify objects. On the other hand, script les must be able to completely redene an object (e.g., to completely replace the behavior of a traits object), so SELF provides the _Define: primitive as a convenience (it could be built out of _AddSlots: and _RemoveSlot:). _Define: rst removes all slots from the receiver, then adds to the receiver all the slots found in its argument object. Thus, after performing the _Define: primitive, the receiver object contains a set of slots with exactly the same names, attributes, and contents as those specied in the _Define: primitives argument object. Like _AddSlots: and _RemoveSlot:, _Define: modies the receiver in place and changes only that one object.

42

How to Use SELF 2.0

How to Write a Program

Self 12> x _Print Object x from the preceding example. <37>: ( | b = nil. c = 3. hello = <a method>. q = <4>. ^ p = nil. b: = <-. _ p: = <-. a = new a. | ) nil Self 13> x _Define: ( | a = 12. j <- 2. | ) <an object> Self 14> x _Print <37>: ( | a = 12. j = 2. j: = <-. | ) nil
Note that xs object reference number is still the same, but its slots have changed.

15 How to Write a Program


15.1 Script Files
A script is a text le containing a sequence of SELF expressions. The _RunScript primitive reads a script le and evaluates its expressions. Script les can be nested (for example, see all.self). The receiver of _RunScript is a string specifying the script le; the sufx .self can be omitted (if _RunScript doesnt nd a le of that name, it adds the sufx .self and tries again). Standard Unix shell conventions le naming conventions can be used (e.g., ../foo.self or ~/MySelfDir/foo.self). The expressions contained in the script le are evaluated as if they had been typed at the prompt unmatched parentheses are used to indicate multi-line expressions as described in section 9 but the expressions in a script le are evaluated in the context of the lobby, not the shell object, and their results are not printed. The expressions in a script le should not be separated by periods (although periods are still required to separate slot denitions in object literals). Parsing errors during a _RunScript of the nature unexpected token... found a . token... usually means that a period was found between expressions in the script le. Suppose the text le hello.self contains these lines:
'hello world' printLine "This is a comment" '10 factorial is ' print 10 factorial printLine
Note: no periods at ends of expressions

This le can be read in (run) using the _RunScript primitive:


> 'hello' _RunScript hello world 10 factorial is 3628800

15.2 Programming Using Script Files


While you may create small objects interactively, most of your programming will be done by editing and reading in script les containing object denitions. The rst time a script le is read in, it

43

How to Use SELF 2.0

How to Write a Program

creates some initial objects. Then, as bugs are discovered, the script le is modied with a text editor and read in again to update the existing objects. This mode of le-based programming is supported by the object manipulation primitives previously discussed plus one additional primitive. The primitive _AddSlotsIfAbsent: adds to the receiver only those slots of its argument object (usually an object literal) that the receiver does not already contain. After performing this primitive, the receiver may contain some new slots, but all slots that previously existed in the receiver are unaffected. This primitive allows script les to be made idempotent: that is, reading them in multiple times has the same affect as reading them in exactly once. The use of _AddSlotsIfAbsent: and _Define: to build objects in script les usually follows the following pattern. The argument to _AddSlotsIfAbsent: is an object literal containing a slot initialized to an empty object. A subsequent _Define: modies this newly-created object to contain the appropriate slots. This pattern allows the programer to repeatedly edit and test an object denition. The rst time the script le is read in, the object to be dened doesnt exist, and the _AddSlotsIfAbsent: creates a new empty object which _Define: lls in. When the script le is subsequently edited and read in again, the _AddSlotsIfAbsent: has no effect (since the slot containing the object already exists) but the _Define: replaces the old version of the object with its new denition. _Define: is used instead of _AddSlots: so that obsolete slots are removed. The combination of _AddSlotsIfAbsent: and _Define: is used instead of a single _AddSlots: operation so that the identity of the edited object is preserved. That is, all references to the old object now point to the new object. For example, if a traits object is redened, then the parent pointers in all clones of its prototype see the behavior dened in the new traits object. Suppose the following denition of a simple point is found in the le point.self:
_AddSlotsIfAbsent: ( | ^ pointTraits = () | ) pointTraits _Define: ( | ^ clone = ( _Clone ). | ) _AddSlotsIfAbsent: ( | ^ pointProto = () | ) pointProto _Define: ( | _ parent* = pointTraits. ^ x <- 0. ^ y <- 0. | )
Define the initial point traits.

Define the prototype.

This le is read in and a new point is cloned:


Self 3> point _RunScript point Self 4> _AddSlots: ( | p1 | ) shell Self 5> p1: ((pointProto clone) x: 5) y: 10 <an object>

44

How to Use SELF 2.0

How to Write a Program

This picture shows the objects that have been created:


pointTraits

clone

...

pointProto

p1

parent* x y 0 0

parent* x y 5 10

Now, suppose the denition of the point traits in the le point.self is changed to:
_AddSlotsIfAbsent: ( | ^ pointTraits = () | ) pointTraits _Define: ( | Redefined point traits. ^ clone = ( _Clone ). ^ print = ( x print. @ print. y print. ). ^ + p = ( (pointProto clone x: x + p x) y: y + p y ). | ) _AddSlotsIfAbsent: ( | ^ pointProto = () | ) pointProto _Define: ( | _ parent* = pointTraits. ^ x <- 0. ^ y <- 0. | )
Define the prototype.

Now this new version is read in:


Self 6> point _RunScript

45

How to Use SELF 2.0

How to Write a Program

This picture shows the resulting object changes:


pointTraits

clone print +

... ... ...

pointProto

p1

parent* x y 0 0

parent* x y 5 10

Reading in the edited script le modied the original pointTraits object in place, instead of creating a new pointTraits object. This allows the new behavior to apply to existing clones of the point prototype such as p1. We can use _Print: to verify this:
Self 7> pointTraits _Print <5>: ( | ^ clone = <a method>. ^ print = <a method>. ^ + = <a method>. | ) nil Self 8> p1 _Print <7>: ( | _ parent* = <5>. ^ x = 5. ^ x: = <-. ^ y = 10. ^ y: = <-. | ) Points to <5>, the newly modified point traits object. nil

15.3 Example: An Electronic Notebook


This section chronicles the evolution of a simple program for making on-line notes. To simplify the presentation, the notebook is kept very simple. The rst step is to create the notebook object, a centralized (unique and global) object that will be the repository for the users notes. Because there will be exactly one notebook object in the system, it should be a one-of-kind objector oddballobject. (Note: The object denitions in this presentation are meant to be entered at the prompt, but they could also be placed in a script le and read in.) Here is the rst version of the notebook object:
oddballs applications _AddSlotsIfAbsent: ( | notebook = () | ) notebook _Define: ( | _ parent* = traits oddball. _ notes <- list copy. ^ addNote: n = ( notes add: n. self ). ^ printNotes = ( notes do: [| :n | n printLine ]. self ). | )

46

How to Use SELF 2.0

How to Write a Program

The notebook object contains both state (notes) and behavior (e.g., printNotes). It inherits some of its behavior from traits oddball. After the notebook object has been created, it can be used to record unstructured notes in the form of strings:
Self 9> notebook addNote: 'This is a short note.' <an object>

Note that addNote: returns the notebook object. This initially prints as <an object> because the notebook object is not in the pathCache. This problem can be corrected by updating the pathCache:
Self 10> pathCache refill refilling path cache...done. '1642 entries' Self 11> notebook addNote: 'SELF is fun!' notebook Self 12> notebook addNote: 'This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes.' notebook

In the last example, the string was typed without carriage returns and allowed to wrap around. If a carriage return were typed in the middle of this string, the parser would consider the expression complete and complain that the string literal is missing its trailing quote mark. The contents of the notebook can be displayed at any time:
Self 13> notebook printNotes This is a short note. SELF is fun! This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes. notebook

The notebook can be improved by having it store structured note objects rather than strings. In particular, note objects can be given short titles to be printed when the user wants to quickly scan the contents of the notebook. Here are the appropriate object denitions:

47

How to Use SELF 2.0

How to Write a Program

traits applications _AddSlotsIfAbsent: ( | note = () | ) traits note _Define: ( | parent* = traits clonable. printString = ( '*** ', title, ' ***\n', contents, '\n' ). | ) prototypes applications _AddSlotsIfAbsent: ( | note = () | ) note _Define: ( | _ parent* = traits note. _ thisObjectPrints = true. ^ title <- '<title>'. ^ contents <- '<note contents>'. | )

The behavior shared by note objects is dened in a separate traits object to allow the behavior of all the note objects to be changed by modifying the traits object. Initially, the traits object just denes a method for printing the object, but later it can be extended as the program evolves. Some of the behavior of note objects is inherited from traits clonable (e.g., copy and printLine). The prototype note object contains the data slots for its title and contents and a parent slot pointing to the note traits object. The constant slot thisObjectPrints tells the read-eval-print loop to print note objects by sending them the printString method (see section 10.2). Finally, to make it convenient to add structured notes and to allow the user to print a list of note titles, the behavior of the notebook object is extended with two additional methods:
notebook _AddSlots: ( | ^ addTitle: t Contents: c = ( notes add: ((note copy title: t) Contents: c). self ). ^ printTitles = ( notes do: [| :n | n title printLine ]. self ). | )

Note that _AddSlots: modies the existing notebook object in place, rather than creating an entirely new one, so the previous notes stored in the notebook are retained. The new notebook can now be used:
Self 14> notebook addTitle: 'Structured Note' Contents: 'this is a structured note' notebook

48

How to Use SELF 2.0

How to Write a Program

Self 15> notebook printNotes This is a short note. SELF is fun! This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes. *** Structured Note *** this is a structured note notebook Self 16> notebook printTitles No 'title' slot found in 'This is a short note.'. Sending method holder is notebook <7>. ## Stack trace for process <15> ...

The last expression, which was meant to print a list of note titles, produced an error because the notebook still contains some old, unstructured notesthat is, simple strings that do not understand the message title. These notes (the rst three added to the notebook) can be removed by manipulating the notebook objects note list directly. The notebook object is rst entered to make its notes slot visible to expressions typed at the prompt (see section 10.2):
Self 17> enter: notebook shell Self 18> notes removeFirst 'This is a short note.' Self 19> notes removeFirst 'SELF is fun!' Self 20> notes removeFirst 'This is a longer note. It goes on for several sentences and may print as multiple lines. Long notes make it difficult to quickly scan a list of notes.'

Now printTitles works correctly:


Self 21> notebook printTitles Structured Note notebook Self 22> notebook addTitle: 'Feature: searching' Contents: 'allow the user to find all notes containing a given substring' notebook Self 53> notebook addTitle: 'Feature: deleting' Contents: 'allow the user to delete a note or a set of notes' notebook

49

How to Use SELF 2.0

How to Write a Program

Self 54> notebook printNotes *** Structured Note *** this is a structured note *** Feature: searching *** allow the user to find all notes containing a given substring *** Feature: deleting *** allow the user to delete a note or a set of notes notebook Self 55> notebook printTitles Structured Note Feature: searching Feature: deleting notebook

You might want to view the structure of this program graphically. To do so, type:
Self 56> ui start initializing ui...creating colormap series . . . . . . . . . done. done. uiX Self 57> ui add: notebook uiX Self 58> ui add: note copy uiX

The previous commands start the graphical user interface and make the notebook and a note object appear in it. Position the cursor over one of the these objects and press the function key F3. This will cause the inheritance hierarchy for the given object to be sprouted and arranged on the screen, as described in section 11. The last two notes in the notebook suggest some possible ways to extend and improve this program. You will probably think of many more. Happy hacking!

50

Vous aimerez peut-être aussi