A User's Guide to Scheme 48 A line may take us hours, yet if it does not seem a moment's thought All our stitching and unstitching has been as nought. Yeats Adam's Curse Introduction Scheme 48 is an implementation of the Scheme programming language as described in the Revised^4 Report on the Algorithmic Language Scheme. It is based on a compiler and interpreter for a virtual Scheme machine. The name derives from our desire to have an implementation that is simple and lucid enough that it looks as if it were written in just 48 hours. We don't claim to have reached that stage yet; much more simplification is necessary. Scheme 48 tries to be faithful to the upcoming Revised^5 Scheme Report, providing neither more nor less in the initial user environment. (This is not to say that more isn't available in other environments; see below.) Support for numbers is weak: bignums are slow and floating point is almost nonexistent (see description of floatnums, below). DEFINE-SYNTAX, LET-SYNTAX, LETREC-SYNTAX, and SYNTAX-RULES are supported, but not the rest of the Revised^4 Scheme macro proposal. The Revised^5 Report hasn't been published yet, but it will be very similar to the Revised^4 Report. For a list of differences, see doc/meeting.tex. This is what might be called an alpha release. Please report bugs, especially in the VM, especially core dumps, to scheme-48-bugs@altdorf.ai.mit.edu. Include the version number x.yy from the "Welcome to Scheme 48 x.yy" greeting message in your bug report. It is a goal of this project to produce a bullet-proof system; we want no bugs and, especially, no crashes. (There are a few known bugs, listed in the TODO file that comes with the distribution.) Send mail to scheme-48-request@altdorf.ai.mit.edu to be put on a mailing list for announcements, discussion, bug reports, and bug fixes. ----- Command line arguments A few command line arguments are processed by the virtual machine as it starts up. scheme48 [-i image] [-h heapsize] [-o filename] [-s stacksize] [-a argument ...] -i image specifies a heap image file to resume. This defaults to a heap image that runs a Scheme command processor. Heap images are created by the ,dump and ,build commands, for which see below. -h heapsize specifies how much space should be reserved for allocation. Heapsize is in words (where one word = 4 bytes), and covers both semispaces, only one of which is in use at any given time (except during garbage collection). Cons cells are currently 3 words, so if you want to make sure you can allocate a million cons cells, you should specify -h 6000000 (actually somewhat more than this, to account for the initial heap image and breathing room). -s stacksize specifies how much space should be reserved for the continuation and environment stack. If this space is exhausted, continuations and environments are copied to the heap. stacksize is in words and defaults to 2500. -o filename This specifies an executable file in which foreign identifiers can be looked up for the foreign function interface. Filename should be the file that contains the scheme48vm executable image. See doc/external.txt. -a argument ... is only useful with images built using ,build. The arguments are passed as a list to the procedure specified in the ,build command. E.g. > ,build (lambda (a) (for-each display a) (newline) 0) foo.image > ,exit % scheme48vm -i foo.image -a mumble "foo x" mumblefoo x % The usual definition of the "s48" or "scheme48" command is actually a shell script that starts up the virtual machine with a -i argument specifying the development environment heap image, and a -o argument specifying the location of the virtual machine. ----- Command processor When you invoke the default heap image, a command processor starts running. At the > prompt, you can type either a Scheme form (expression or definition), or a command beginning with a comma. Logistical commands: ,load ... load Scheme source file(s) Easier to type than (load "filename") because you don't have to shift to type the parentheses or quote marks. Also, it works in any package, unlike (load "filename"), which will work only work in packages in which the variable LOAD is defined properly. ,exit [] leave Exit back out to shell (or executive or whatever invoked Scheme 48 in the first place). should evaluate to an integer. The integer is returned to the calling program. (On Unix, 0 is generally interpreted as success, nonzero as failure.) Command levels: If an errors occurs, you are put in a command loop at the dynamic point at which the error occurred. The prompt will then be "n >" where n is the command level nesting depth. To pop out one level (running any dynamic-wind "after" thunks), send an end-of-file (usually control-D at a Unix shell or using the Emacs "cmuscheme48" library). ,reset top level Unwind all the way back out to top level. ,level go to command level Unwind out to a given level. ,level 0 is the same as ,reset. ,push Go to a deeper command level. (See ,levels, below.) Debugging commands: ,preview Sort of like a backtrace, but because of tail recursion you see less than you might in debuggers for some other languages. ,proceed ... Proceed after an interrupt or error, delivering the values of ... to the continuation. ,trace ... Start tracing calls to the named procedure or procedures. With no arguments, displays all procedures currently traced. This affects the binding of , not the behavior of the procedure that's it's current value. The effect is similar to (define (make-traced )) where make-traced is a procedure-returning procedure. ,untrace ... Stop tracing calls to the named procedure or procedures. With no argument, stop tracing all calls to all procedures. ,condition The ,condition command selects and displays the condition object describing the error or interrupt that initiated the current command level. This is particularly useful in conjunction with the inspector. E.g. if a procedure is passed the wrong number of arguments, do ,condition followed by ,inspect ## to inspect the procedure and its arguments. ,bound? Display the binding of , if there is one. ,expand
Show macro expansion of , if any. ,where Display name of source file in which is defined. Building images: ,dump [] This writes out the current heap. When the new image is resumed, it starts in the command processor. If present, should be a string (written with double quotes); this string will be part of the greeting message as the image starts up. ,build should evaluate to a procedure of one argument. When is resumed, that procedure will be invoked on the VM's -a arguments, which are passed as a list of strings. The procedure should return an integer (as for ,exit). The command processor and debugging system are not included in the image (unless you go to some effort to preserve them, such as retaining a continuation). Doing ",flush" before building an image will make for smaller images, but if an error occurs, the error message may be less helpful. Doing ",flush source maps" before loading any programs will make the image still smaller. Modes: When given no argument, all of these commands toggle the corresponding mode. With the argument ?, the current setting is displayed. Otherwise the argument should be ON or OFF. ,batch [on | off | ?] In "batch mode," any error or interrupt that comes up will cause Scheme 48 to exit immediately with a non-zero exit status. Also, the command processor doesn't print prompts. The default is interactive mode. ,form-preferred [on | off | ?] Enable or disable "form preferred" mode. In this mode, command processor commands needn't be prefixed by comma. To see the value of a variable (or number - this should be fixed), do (begin ). "Command preferred" mode is the default. ,levels [on | off | ?] Enable or disable command levels. With levels enabled (the default), errors "push" a new command level, and (see above) or ,reset is required to return to top level. The effects of pushed command levels include: - a longer prompt - retention of the continuation in effect at the point of errors - longer ,previews - confusion among some newcomers With levels disabled, one must issue a ,push command immediately following an error in order to retain the error continuation for debugging purposes; otherwise the continuation is lost after the next evaluation request. If you don't know anything about the available debugging tools, then levels might as well be disabled. This is an experimental feature inspired by gripes about how confusing recursive command loop levels are to newcomers to Scheme. Let me know (jar@ai.mit.edu) if you like it; otherwise it might get flushed. ,break-on-warnings [on | off | ?] When a warning is produced, enter a new command level, just as when an error occurs. Resource query and control: ,time Measure execution time. ,collect Invoke the garbage collector. Ordinarily this happens automatically, but the command tells how much space is available before and after the collection. ,keep ,flush These control the amount of debugging information retained after compiling procedures. This information can consume a fair amount of space. is one of the following: . maps - environment maps (local variable names, for inspector) . source - source code for continuations (displayed by inspector) . names - procedure names (as displayed by WRITE and in error messages) . files - source file names These commands refer to future compilations only, not to procedures that already exist. To have any effect, they must be done before programs are loaded. ,flush The flush command with no argument deletes the database of names of initial procedures. Doing ",flush" before a ,build or ,dump will make the resulting image significantly smaller (by up to 200K bytes), but will compromise the information content of many error messages. Quite obscure: ,go This is like ,exit except that the evaluation of is tail-recursive with respect to the command processor. This means that the command processor itself can probably be GC'ed, should a garbage collection occur in the execution of . Any errors will be treated as in batch mode. ,translate For LOAD and the ,load command (but not for OPEN-xxPUT-FILE), file names beginning with the string will be changed so that the initial is replaced by the string . E.g. ,translate /usr/gjc/ /zu/gjc/ will cause (load "/usr/gjc/foo.scm") to have the same effect as (load "/zu/gjc/foo.scm"). ,from-file ... ,end This is used by the cmuscheme48 Emacs library. Other commands are (or should be) described in the module system document. ----- Editing We recommend running Scheme 48 under Gnu Emacs using the cmuscheme48 command package. This is in the Scheme 48 distribution's emacs/ subdirectory. It is a variant of the "cmuscheme" library, which comes to us courtesy of Olin Shivers, formerly of CMU. You might want to put the following in your emacs init file (.emacs): (setq scheme-program-name "scheme48") (autoload 'run-scheme "cmuscheme" "Run an inferior Scheme process." t) To make the autoload and (require ...) forms work, you will also need to put the directory containing cmuscheme and related files in your emacs load-path: (setq load-path (append load-path '("/emacs"))) For further documentation see emacs/cmuscheme.el and emacs/comint.el. ----- Performance If you want to generally have your code run faster than it normally would, enter "benchmark mode" before loading anything. Otherwise calls to primitives (like + and cons) and in-line procedures (like not and cadr) won't be open-coded, and programs will run more slowly. Enter benchmark mode by issuing the ,bench command to the command processor. The system doesn't start in benchmark mode by default because the Scheme report permits redefinitions of built-in procedures. In benchmark mode, such redefinitions don't work according to the report, because previously compiled calls may have in-lined the old definition, leaving no opportunity to call the new definition. ",bench" toggles benchmark mode. ",bench on" and ",bench off" turn it on and off. ----- Inspector There is a low-tech inspector available via the ,inspect and ,debug commands. The ,inspect command starts an inspector command loop. There is a focus object (the same as the command processor's ##), for which a menu of selectable components is displayed. To inspect a particular component, just type the corresponding number in the menu. For example: ,inspect '(a (b c) d) (a (b c) d) [0] a [1] (b c) [2] d inspect: 1 (b c) [0] b [1] c inspect: When a new object is selected, the previous one is pushed onto a stack. You can pop the stack, reverting to the previous object, with the U command. The inspector is particularly useful with procedures, continuations, and records. Other inspector commands: u pop object stack d down stack (current object must be a continuation) m print more of a long menu (...) evaluate a form and select result t select a closure or continuation's template q quit ## is always the object currently being inspected. After a Q command, or an error in the inspector, ## is the last object that was being inspected. The inspector also accepts arbitrary command processor commands, e.g. the ,dis command (see below). The leading comma is optional. After an error occurs, ,debug invokes the inspector on the continuation at the point of the error. The U and D (up and down) commands then make the inspector look like a conventional stack debugger, with continuations playing the role of stack frames. D goes to older or deeper continuations (frames), and U goes back up to more recent ones. Templates are the static components of procedures; these are found inside of procedures and continuations, and contain the quoted constants and top-level variables referred to by byte-compiled code. ----- Disassembler The ,dis command disassembles procedures. > ,dis cons cons 0 (check-nargs= 2) 2 (pop) 3 (make-stored-object 2 pair) 6 (return) > The command argument is optional; if unsupplied it defaults to the current focus object (##). The disassembler can also be invoked on continuations and templates. ----- Module system For information on the module (package) system, see doc/module.tex. ----- Library A number of useful utilities are either built in to Scheme 48 or can be loaded from an external library. These utilities are not visible in the user environment by default, but can be made available with the ,open command. For example, to use the tables structure, do > ,open tables > If the utility is not already loaded, then the ,open command will offer to load it: > ,open queues Load structure queues (y/n)? Or, you can load something explicitly (without opening it) using the load-package command: > ,load-package queues ... > ,open queues When loading a utility, the message "Note: optional optimizer not invoked" is innocuous. Feel free to ignore it. See also the package system documentation, doc/module.tex. Unfortunately, few of these wonderful things are documented. They are listed, however, in files rts-packages.scm, comp-packages.scm, and more-packages.scm in the distribution directory, and the bindings they export are listed in interfaces.scm and more-interfaces.scm. Here is a little information on the more generally useful structures. architecture Information about the virtual machine. E.g. (enum op eq?) => the integer opcode of the EQ? instruction arrays Arrays. See comments at the top of file big/array.scm. ascii CHAR->ASCII and ASCII->CHAR. Similar to CHAR->INTEGER and INTEGER->CHAR except that ASCII encoding is guaranteed. big-scheme Many generally useful features. See doc/big-scheme.txt. bigbit Extensions to the bitwise logical operators (exported by the BITWISE structure) so that they operate on bignums. To use these you should do ,load-package bigbit ,open bitwise bitwise Bitwise logical operators. See doc/big-scheme.txt. conditions Condition system: DEFINE-CONDITION-PREDICATE and routines for examining condition objects. (See also handle, signals.) define-record-types A define-record-type macro, providing more concise use of the record package. (Richard and Jonathan favor different define-record-type macros; this one is Jonathan's.) defpackage The module system: DEFINE-STRUCTURE and DEFINE-INTERFACE. defrecord A define-record-type macro, providing more concise use of the record package. (Richard and Jonathan favor different define-record-type macros; this one is Richard's.) destructuring DESTRUCTURE macro. See doc/big-scheme.txt. display-conditions Displaying condition objects. (DISPLAY-CONDITION condition port) => unspecific Display condition in an easily readable form. E.g. > ,open display-conditions handle conditions > (display-condition (call-with-current-continuation (lambda (k) (with-handler (lambda (c punt) (if (error? c) (k c) (punt))) (lambda () (+ 1 'a))))) (current-output-port)) Error: exception (+ 1 'a) > enumerated Enumerated types. See doc/big-scheme.txt. extended-ports Ports for reading from and writing to strings, and related things. See doc/big-scheme.txt. externals Rudimentary external function interface. See doc/external.txt. filenames Rudimentary file name parsing and synthesis. E.g. file-name-directory and file-name-nondirectory are as in Gnu emacs. floatnums Floating point numbers. These are in a very crude state; use at your own risk. They are slow and do not read or print correctly. fluids Dynamically bound "variables." (MAKE-FLUID top-level-value) => fluid (FLUID fluid) => value (SET-FLUID! fluid value) => unspecific (LET-FLUID fluid value thunk) => whatever thunk returns formats A simple FORMAT procedure, similar to Common Lisp's or T's. general-tables An extended version of TABLES; supports tables keyed by strings. See doc/big-scheme.txt. handle Part of the condition system. (WITH-HANDLER handler thunk) => whatever thunk returns handler is a procedure of two arguments. The first argument is a condition object, and the second is a "punt" procedure. The handler should examine the condition object (using ERROR?, etc. from the CONDITIONS structure). If it decides not to do anything special, it should tail-call the "punt" procedure. Otherwise it should take appropriate action and perform a non-local exit. It should not just return unless it knows damn well what it's doing; returns in certain situations can cause VM crashes. interrupts Interrupt system more-threads Interface between multitasking and the command processor. Try this: ,open threads more-threads (start-threads) (spawn (lambda () (display "Hello "))) Cf. doc/threads.txt. ports A few extra port-related operations, notably FORCE-OUTPUT. pp A pretty-printer. (p ) will pretty-print the result of , which must be an S-expression. (Source code for procedures is not retained or reconstructed.) You can also do (p ) to print to a specific port. The procedure pretty-print takes three arguments: the object to be printed, a port to write to, and the current horizontal cursor position. If you've just done a newline, then pass in zero for the position argument. The algorithm is very peculiar, and sometimes buggy. queues FIFO queues. random Random number generator. > (define random (make-random )) > (random) => a pseudo-random number between 0 and 2^28 receiving Convenient interface to the call-with-values procedure, like Common Lisp's multiple-value-bind macro. records MAKE-RECORD-TYPE and friends. See the Scheme of Things column in Lisp Pointers, volume 4, number 1, for documentation. recnums Complex numbers. This should be loaded (e.g. with ,load-package) but needn't be opened. search-trees Balanced binary search trees. See comments at top of big/search-tree.scm. signals ERROR, WARN, and related procedures. sort Online merge sort (see comment at top of file big/sort.scm). (sort-list ) (sort-list! ) sicp Compatibility package for the Scheme dialect used in the book "Structure and Interpretation of Computer Programs." sockets Interface to Unix BSD sockets. See comments at top of file misc/socket.scm. tables Hashed association tables. Keys are compared using EQ?. (MAKE-TABLE [hash-function]) => table Default hash function accepts symbols and integers. (TABLE-REF table key) => entry (TABLE-SET! table key entry) => unspecific (TABLE? thing) => boolean (TABLE-WALK proc table) => unspecific Proc should take two arguments, a key and the ocrresponding entry. threads Multitasking. See doc/threads.txt. util SUBLIST, ANY, REDUCE, FILTER, and some other useful things. weak Weak pointers and populations. (MAKE-WEAK-POINTER thing) => weak-pointer (WEAK-POINTER-REF weak-pointer) => thing or #F #F if the thing has been gc'ed. writing (RECURRING-WRITE thing port recur) => unspecific This is the same as WRITE except that recursive calls invoke the recur argument instead of WRITE. For an example, see the definition of LIMITED-WRITE in env/dispcond.scm, which implements processing similar to common Lisp's *print-level* and *print-length*. ----- Acknowledgment Thanks to Deborah Tatar for providing the Yeats quotation.