Wraith Scheme Help File

Main Window

Wraith Scheme Help File

For Wraith Scheme 2.27

Copyright © 1988-2022 Jay Reynolds Freeman, all rights reserved.
Personal Web Site: http://JayReynoldsFreeman.com
EMail: Jay_Reynolds_Freeman@mac.com.


Run mad as often as you chuse, but do not faint. -- Jane Austen



WARRANTY, LICENSE, AND DISTRIBUTION:

APPLICABILITY OF THIS DOCUMENT:


Foreign-Function Interface:

Wraith Scheme provides two related capabilities that together allow other programs -- that is, other Macintosh applications or other Unix processes -- (1) to share data with Wraith Scheme and (2) to interrupt Wraith Scheme when necessary; for example, when they have new data ready to share.

These two capabilities are probably the most complicated and bewildering of Wraith Scheme's enhancements. They will probably only be of interest to users who are at least moderately experienced programmers in other programming languages, and who have at least some familiarity with Unix system calls and computer architecture. In particular, knowledge of memory-mapping and of hardware and software interrupts is almost certainly required to understand and to use these enhancements.


Overview of the Foreign-Function Interface:

The intent is that data can be shared by way of shared memory areas created using the Unix "mmap" function. Wraith Scheme has a variety of procedures that allow Wraith Scheme to create memory-mapped areas associated with files that you select. Those files may be read or written by any Wraith Scheme process -- MomCat or kitten. You can then create other programs which also memory-map the same files, and those other programs can then thereby share data with Wraith Scheme.

The interrupt system allows other processes to signal Wraith Scheme when they have something for it to do. It is based on the Unix "signal" capability, and uses the signal called "SIGUSR1". It also uses another area of shared memory, which contains a table of flags indicating what a Wraith Scheme process should do when it receives a SIGUSR1 signal. There are 1024 such flags, so that in effect, there are 1024 different interrupts. That same area of shared memory also contains a table of the Unix process identifications (PIDs) of the MomCat and kittens, which any other process may use to figure out just where to send the SIGUSR1.

Wraith Scheme itself contains a table of 1024 interrupt handlers, stored in Wraith Scheme main memory, that tell what to do for each interrupt. Each handler tells what kitten is expected to handle the interrupt -- and that need not be the same as the kitten that received the SIGUSR1 -- and an S-expression which that kitten is supposed to evaluate to deal with the interrupt. You may set interrupt handlers at will.

The way a kitten receiving a SIGUSR1 responds to that signal, is by adding the given S-expression into the input queue of the kitten that is supposed to handle it. The interrupt will not be handled until that latter kitten is waiting in its top-level loop with nothing else to do. Interrupts are also disabled during garbage collection.

Note carefully, that two separate actions are required for Wraith Scheme to deal with an interrupt:

  • A Wraith Scheme process -- call it process A -- receives a SIGUSR1 signal, and in consequence appends an S-expression to the input queue of another Wraith Scheme process -- call it process B. I call this action "responding to an interrupt".

  • Wraith Scheme process B evaluates the S-expression that process A placed in its input queue. I call this action "handling an interrupt".

    A and B may be the same process, though it is probably better that they be different.

The Wraith Scheme process that responds to the interrupt may or may not be the one that handles it.

This mechanism provides what I might describe as a "coarse-grained" foreign-function interface: It is technically possible to use the interface to allow Wraith Scheme to make lots of calls to simple functions that run quickly. Yet it is probably more useful to use it for foreign functions that do substantial autonomous work, such as processing large data structures at Wraith Scheme's behest, or preparing large data structures for Wraith Scheme's perusal.


Some Background for mmap:

The Unix "mmap" function is in my opinion poorly documented and seems to be little used, or at least little understood. Its original purpose was evidently to direct the operating system to copy an entire file into a contiguous area of a process's address space, so that its contents could be read and written as elements of a big array, or perhaps a big struct, instead of forever having to seek back and forth in the file and use functions like "read" and "write" to access data. For many uses of files, this mmap interface much simplifies and speeds up file access.

When the file is loaded in, the operating system retains the ability to "page out" portions of the file that are little used. In doing so, the operating system uses the file itself as the "swap space" for the paged-out portions.

More than one process can use the same mmapped file. The operating system keeps track of the actual physical address of the file image, maps an appropriate portion of each process's virtual address space to the physical image of the file, and tells the process where it is via the value returned from "mmap" itself. This mechanism is very useful -- perhaps when the file is a big database that is required by several different processes. The processes that use the file have to make sure to use an appropriate protocol that will not get the database mixed up because of near-simultaneous writes and so on, but they would have to do that even if they were accessing the file on a disc.

When two processes use "mmap" to share memory, they kind of use the function backwards from its original intent. One of the processes creates a scratch file of the appropriate length, and writes it full of zeros (or whatever). Then all of the processes mmap the scratch file to their own address space, wherein it becomes an area of memory that is shared by all processes.


Details and Procedures for Sharing Memory:

Wraith Scheme encapsulates information about shared memory areas in Wraith Scheme objects called "memory-mapped blocks", which are stored in Wraith Scheme main memory. Notwithstanding the name, such a Wraith Scheme object does not actually contain a memory-mapped area, just a pointer to it, along with other information about the block in question.

When several Wraith Scheme processes -- a MomCat and some kittens -- are running in parallel, they are all separate Unix processes. Therefore, even though the "memory-mapped block" object is itself stored in Scheme main memory, where any of those Wraith Scheme processes can get at it, actual the memory-mapped area, to which the memory-mapped block points, is not accessible to any given Wraith Scheme process until that process itself has used the Unix "mmap" function to make the block available to itself.

There is one Wraith Scheme procedure, "e::make-memory-mapped-block", to memory-map the block the first time (in which case a file associated with it is also created and written full of zeros). That procedure memory-maps the block only in the Wraith Scheme process which executes it, and returns a memory-mapped block object describing and pointing to the actual area that has been mapped. A second Wraith Scheme procedure, "e::memory-map-block-in-current-kitten", takes a pre-existing instance of memory-mapped block as an argument, and uses the information stored in it to memory-map the block anew in whatever Wraith Scheme process is executing that second primitive. In this way, a given memory-mapped block may be made available to as many Wraith Scheme processes as need it.

The reason why the first of those two primitives does not immediately memory-map the block in all extant Wraith Scheme processes is that there is no way for one Wraith Scheme process to get another Wraith Scheme process to do something immediately. You could be misled into thinking that all Wraith Scheme processes had memory-mapped the block when in fact they had not.

The memory regions used for these blocks are allocated from a large area of Wraith Scheme shared memory that is reserved when Wraith Scheme starts running. The allocation mechanism is trivial -- it simply goes straight through the available area until it runs out. There is no provision to "free" the memory in such a memory-mapped blocks.

The amount of memory available for these blocks depends on whether you are using a Macintosh with Intel processors or one with Apple's proprietary processors ("Apple silicon"): In the former, it is about 150 MByte, and in the latter it is more than 100 GByte. (The reason for the difference has to do with how Apple's programming software reserves memory for the two different processor types.)


Utility Procedures for Memory-Mapped Blocks:

Utility procedures that deal with memory-mapped blocks follow. See the more detailed descriptions in the Wraith Scheme Dictionary.

    (e::make-memory-mapped-block <size in bytes> <path to associated file>)
    

      Create and return a memory-mapped block of the given size, associated with the given file, memory-mapped in the Wraith Scheme process that executed the procedure, with the file written full of zeros.

    (e::memory-map-block-in-current-kitten <a memory-mapped block>)
    

      Memory-map an existing memory-mapped block in the Wraith Scheme process that is executing the procedure, which is presumably one in which that block has not already been mapped. Associate the block with the same file with which it was originally mapped, but do not change the file.

    (e::memory-mappable-space-size)
    

      Return the size in bytes of that portion of Wraith Scheme's pre-reserved block of mappable memory that has not yet been allocated.

    (e::make-offsets <start-offset> <list of variable names and sizes>)
    

      Given a starting offset, and a list of pairs of variable names and sizes, define new Scheme variables using the given names, that point to the given locations. The names of the new variables are obtained by appending "-offset" to the variable names given. Returns the total of all the sizes.

      This procedure creates pointers that other procedures can use to reference memory in mmapped blocks, but this procedure itself does not attempt to read or write anything that the pointers point to.

    (e::memory-mapped-block? <object>)
    

      Is the object a memory-mapped block?

    Technical Note: The addresses of the memory-mapped blocks that Wraith Scheme allocates will all be page-aligned, as required by the Unix "mmap" function.


Procedures for Accessing Memory-Mapped Blocks:

There are many procedures for accessing memory-mapped blocks. They include (1) a procedure for atomic test-and-set within memory-mapped blocks, (2) procedures for reading and writing four lengths of integer data, one at a time or in vectors, with optional sign-extension, (3) procedures for reading and writing floating-point numbers, one at a time or in vectors, (4) procedures for reading and writing "C"-style (null-terminated) strings, one at a time, and (5) procedures for converting back and forth between C-style booleans on the one hand, and #t and #f on the other.

The first procedure provides atomic test-and-set of the least significant bit in a 64-bit integer in a memory-mapped block.

    (e::peek-poke-atomic <memory-mapped block> <offset>)
    

Returns whether the bit was zero before the test-and-set operation. If several processes attempt this operation at the same time, at most one will find that the bit was zero.

The next four procedures provide read access to memory-mapped blocks for individual data elements. They are perhaps most useful if the data are integers.

    (e::peek8 <memory-mapped block> <offset>)
    (e::peek16 <memory-mapped block> <offset>)
    (e::peek32 <memory-mapped block> <offset>)
    (e::peek64 <memory-mapped block> <offset>)
    

      Return the content of the given location, as a 64-bit integer, not sign-extended.

Each is called with a memory-mapped block as its first argument, and an offset in bytes from the beginning of the block, at which the read is to take place, as its second argument. The reads are respectively of quantities 8, 16, 32 and 64 bits in length -- that is, one, two, four and eight bytes -- and the offsets must be integral multiples of those numbers of bytes. The quantities returned are 64-bit integers, but the data returned are not sign extended. Thus a peek8 of a byte with all bits on returns 255, not -1.

Note that there is no requirement that the content addressed actually be intended as an integer; Wraith Scheme has no idea of what other programs may be storing at that location.


The next three procedures also provide read access to memory-mapped blocks for individual data elements. They are like the procedures immediately above, except that the three below sign-extend the result when converting it to a 64-bit signed integer.

    (e::peek8-sign-extend <memory-mapped block> <offset>)
    (e::peek16-sign-extend <memory-mapped block> <offset>)
    (e::peek32-sign-extend <memory-mapped block> <offset>)
    

      Return the content of the given location, as a 64-bit integer, sign-extended.

Each is called with a memory-mapped block as its first argument, and an offset in bytes from the beginning of the block, at which the read is to take place, as its second argument. The reads are respectively of quantities 8, 16 and 32 bits in length -- that is, one, two and four bytes -- and the offsets must be integral multiples of those numbers of bytes. The quantities returned are 64-bit integers, and the data returned are sign extended. Thus a peek8-sign-extend of a byte with all bits on returns -1, not 255.

Note that there is no requirement that the content addressed actually be intended as an integer; Wraith Scheme has no idea of what other programs may be storing at that location.


The next four procedures provide read access to memory-mapped blocks for vectors of data. They load a Scheme vector with data from a vector in a memory-mapped block. They are perhaps most useful if the data are integers.

    (e::peek8-set-vector! <memory-mapped block> <offset> <vector>)
    (e::peek16-set-vector! <memory-mapped block> <offset> <vector>)
    (e::peek32-set-vector! <memory-mapped block> <offset> <vector>)
    (e::peek64-set-vector! <memory-mapped block> <offset> <vector>)
    

      Return the content of the given locations, as a vector of 64-bit integers, not sign-extended, by modifying the given vector and returning it.

Each is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the read is to begin, as its second argument, and a vector as its third argument. The number of data elements to be read is presumed to be the length of the vector that is passed as the last argument to the procedure. The individual reads are respectively of quantities 8, 16, 32 and 64 bits in length -- that is, one, two, four and eight bytes -- and the offsets must be integral multiples of those numbers of bytes. The quantities returned are 64-bit integers, but the data returned are not sign extended. Thus a byte with all bits on is read as 255, not -1. The data read are stored in the given vector, and that vector is returned as modified.

Note that there is no requirement that the content addressed actually be intended as integers; Wraith Scheme has no idea of what other programs may be storing at that location.


The next three procedures also provide read access to memory-mapped blocks for vectors of data. They are like the procedures immediately above, except that the three below sign-extend the results when converting them to 64-bit signed integers.

    (e::peek8-set-vector-sign-extend! <memory-mapped block> <offset> <vector>)
    (e::peek16-set-vector-sign-extend! <memory-mapped block> <offset> <vector>)
    (e::peek32-set-vector-sign-extend! <memory-mapped block> <offset> <vector>)
    

      Return the content of the given locations, as a vector of 64-bit integers, sign-extended, by modifying the given vector and returning it.

Each is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the read is to begin, as its second argument, and a vector as its third argument. The number of data elements to be read is presumed to be the length of the vector that is passed as the last argument to the procedure. The individual reads are respectively of quantities 8, 16 and 32 bits in length -- that is, one, two and four bytes -- and the offsets must be integral multiples of those numbers of bytes. The quantities returned are 64-bit integers, and the data returned are sign extended. Thus a byte with all bits on is read as -1. The data read are stored in the given vector, and that vector is returned as modified.

Note that there is no requirement that the content addressed actually be intended as an integer; Wraith Scheme has no idea of what other programs may be storing at that location.


The next four procedures provide write access to memory-mapped blocks for individual integers.

    (e::poke8 <memory-mapped block> <offset> <integer>)
    (e::poke16 <memory-mapped block> <offset> <integer>)
    (e::poke32 <memory-mapped block> <offset> <integer>)
    (e::poke64 <memory-mapped block> <offset> <integer>)
    

      Store the integer given at the indicated offset in the memory-mapped block provided.

Each is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the write is to take place, as its second argument, and an integer to be placed there as its third argument. The writes are respectively of quantities 8, 16, 32 and 64 bits in length -- that is, one, two, four and eight bytes -- and the offsets must be integral multiples of those numbers of bytes. The integers to be written must be in the range appropriate for a 64-bit fixnum, and will silently have their more significant bits removed in sufficient quantity that the result fits in the space provided.


The next four procedures provide write access to memory-mapped blocks for vectors of integers.

    (e::poke8-set-vector! <memory-mapped block> <offset> <vector>)
    (e::poke16-set-vector! <memory-mapped block> <offset> <vector>)
    (e::poke32-set-vector! <memory-mapped block> <offset> <vector>)
    (e::poke64-set-vector! <memory-mapped block> <offset> <vector>)
    

      Store the contents of the given vector at the given location in the memory-mapped block provided.

Each is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the write is to begin, as its second argument, and a vector of integers to be written as its third argument. The number of data elements to be written is presumed to be the length of the vector that is passed as the last argument to the procedure. The individual writes are respectively of quantities 8, 16, 32 and 64 bits in length -- that is, one, two, four and eight bytes -- and the offsets must be integral multiples of those numbers of bytes. The integers to be written must be in the range appropriate for a 64-bit fixnum, and will silently have their more significant bits removed in sufficient quantity that the result fits in the space provided.


The next procedure provides read access to memory-mapped blocks for individual floating-point numbers.

    (e::peek64-float <memory-mapped block> <offset>)
    

      Reads the memory-mapped block at the given offset, interprets the data there as a 64-bit flonum, and returns that number.

This procedure is called with a memory-mapped block as its first argument, and an offset in bytes from the beginning of the block, at which the write is to take place, as its second argument. The offset must be a multiple of eight.


The next procedure provides write access to memory-mapped blocks for individual floating-point numbers.

    (e::poke64-float <memory-mapped block> <offset> <number>)
    

      Stores the given number as a 64-bit floating point number at the given offset of the given memory-mapped block.

This procedure is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the write is to take place, as its second argument, and a number as its third argument. The offset must be a multiple of eight. It does not matter whether the third argument is stored as a fixnum or a flonum; Wraith Scheme will convert any fixnums to flonums before performing the store.


The next procedure provides read access to memory-mapped blocks for vectors of floating-point numbers.

    (e::peek64-float-vector <memory-mapped block> <offset> <vector>)
    

      Reads the memory-mapped block at the given offset, interprets the data there as a vector of 64-bit flonums, and loads and returns the given Scheme vector with those flonums.

This procedure is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the read is to begin, as its second argument, and a vector as its third argument. The offset must be a multiple of eight. The number of flonums to be read is presumed to be the length of the given vector. The data read are stored in the given vector, and that vector is returned as modified.


The next procedure provides write access to memory-mapped blocks for vectors of floating-point numbers.

    (e::poke64-float-set-vector! <memory-mapped block> <offset> <vector>)
    

      Stores the given vector of numbers at the given offset of the given memory-mapped block, as 64-bit flonums.

This procedure is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the write is to take place, as its second argument, and a vector as its third argument. The offset must be a multiple of eight. The number of flonums to be written is presumed to be the length of the given vector. It does not matter whether the elements of the given Scheme vector are stored as a fixnums or flonums; Wraith Scheme will convert any fixnums to flonums before performing the stores. The data read are stored in the given vector, and that vector is returned as modified.


The next procedure provides read access to memory-mapped blocks for null-terminated strings. Strings of this form are commonly used in C, C++, and many other programming languages.

    (e::peek-c-string <memory-mapped block> <offset>)
    

      Reads the memory-mapped block at the given offset, interprets the data there as a null-terminated string of ASCII characters, and returns a Scheme string containing that text.

This procedure is called with a memory-mapped block as its first argument, and an offset in bytes from the beginning of the block, at which the read is to take place, as its second argument.

If any subsequent action should change the content of the given memory-mapped block at the given offset, the Scheme string already returned by this procedure will not change; it contains a copy of the original data, not some kind of reference or pointer to the original data.


The next procedure provides write access to memory-mapped blocks for null-terminated strings. Strings of this form are commonly used in C, C++, and many other programming languages.

    (e::poke-c-string <memory-mapped block> <offset> <string>)
    

      Stores a null-terminated string containing the characters from the given string, at the given offset of the given memory-mapped block.

This procedure is called with a memory-mapped block as its first argument, an offset in bytes from the beginning of the block, at which the write is to take place, as its second argument, and a string as its third argument. There must be sufficient space in the memory-mapped block, after the given offset, for the null-terminated string to fit.


The next two procedures convert back and forth between C-style booleans and Scheme objects. They are useful for dealing with foreign booleans.

    (e::c-boolean->scheme-boolean <number>)
    

      Returns #f if the given number is zero, otherwise returns #t

    (e::scheme-boolean->c-boolean <Scheme object>)
    

      Returns 0 if the given object is #f, otherwise returns 1.


Details and Procedures for the Interrupt System:

The Wraith Scheme Cocoa implementation actually has three slightly different interrupt mechanisms in place and available to users. They are all based on the Unix "signal" software interrupts. Only one is used for the foreign-function interface, but I will describe the others here as well.


SIGINT Interrupts:

The first interrupt mechanism is a very simple signal-handler for the Unix "SIGINT" interrupt, that resets Wraith Scheme when that signal is received. It has actually been in Wraith Scheme for a long time, even in most of the 32-bit versions; I just forgot to document it. If anything sends the SIGINT interrupt to any Wraith Scheme process, that process will probably reset itself to the top-level loop soon. If it does not, the problem is most likely an implementation error, and my fault.

To use this mechanism, imagine that you have used the Unix "ps" command, or some other means, to determine the Unix process identification number of a Wraith Scheme process. Suppose for purposes of discussion that that process identification is 12345. Then, bearing in mind that the literal value of SIGINT is 2, you might type

    kill -2 12345
    

in a Terminal shell. That Wraith Scheme process should shortly reset itself.

Incidentally, SIGINT is what is typically sent to Unix command-line processes when the user types "control-C". Note, however, that control-C doesn't work for Macintosh applications -- typing control-C to Wraith Scheme will not make it stop running.


SIGALRM Interrupts:

The second interrupt mechanism involves a signal-handler for the Unix "SIGALRM" interrupt, and some procedures to use the underlying Unix "alarm" mechanism, which in turn will use this interrupt.

The procedures that deal with this mechanism are:

    (e::alarm)
    

      This procedure is actually not defined in Wraith Scheme. You get to define it yourself, as a lambda expression with no arguments, to do whatever you want to have happen when an "alarm" interrupt occurs.

    (e::alarm?)
    

      Returns whether an alarm is presently set.

    (e::alarm-time)
    

      Returns the Unix time at which any presently set alarm will go off. If no alarm is set, returns -1.

    (e::set-alarm! <non-negative integer>)
    

      If the integer is positive, sets an alarm to go off that number of seconds in the future. If the integer is zero, turns off any alarm that has already been set. Normally returns #t, but may return #f if any of the underlying Unix system calls, that are used to set up the alarm, should fail.

You may use the procedure "e::set-alarm!" to cause Wraith Scheme to interrupt itself at a time in the future; when that time occurs, the procedure "e::alarm" will run. You may also use the procedures "e::alarm" and "e::alarm-time" respectively to tell whether an alarm is presently set and at what time (Unix time) it will occur.

You may also cause "e::alarm" to run by sending a "SIGALRM" signal to Wraith Scheme from some other program.

Use of these procedures makes it possible to start something happening automatically at a specified time in the future. These procedures provide a rather straightforward interface to the Unix "alarm" system call, which is the mechanism Wraith Scheme uses for implementing them.

The alarm is made to go off by pushing the S-expression '(e::alarm) into the input queue of the Wraith Scheme kitten in which the alarm was set. If that kitten is busy doing something else at the time, the alarm will not be processed until the kitten is free. Thus the alarm system does not provide a truly asynchronous interrupt; for an alarm to happen promptly you must run it in a kitten that is not busy.

If the symbol "e::alarm" is not bound to a value, or is not bound to a lambda expression, then no alarm will go off. Wraith Scheme will report an error if an alarm goes off while "e::alarm" is bound to a lambda expression that requires arguments.


SIGUSR1 Interrupts:

The third interrupt mechanism is more general and more powerful: It uses the Unix "SIGUSR1" interrupt, and provides a way whereby any Unix process -- such as any application -- may send an interrupt to any Wraith Scheme process, and in so doing may indicate which of a user-selected variety of interrupt handlers will run in response to the interrupt. As I write these words, there are 1024 logically distinct interrupts, any combination of which may be requested at the same time, and each such interrupt may have its own, different handler, in the form of Wraith Scheme code that you specify. The mechanism to do these things has several parts that you may use.

To begin with, there is a region of memory-mapped shared memory, associated with a publicly known binary file, that contains a table of 1024 boolean flags used to specify what interrupts are requested. The idea is that any program that needs to interrupt Wraith Scheme may memory-map that file into its own address space, set whatever flag corresponds to the interrupt it needs, and then send signal SIGUSR1 to some Wraith Scheme process. As an aid to finding out the Unix process identifications (PIDs) of Wraith Scheme processes, that piece of memory-mapped shared memory also contains a table of all the PIDs of all running Wraith Scheme processes, indexed by kitten number. The file also contains a table of flags that show whether an interrupt handler is installed or not.

Wraith Scheme automatically maintains the table of PIDs and the table of flags that shows whether interrupt handlers have been installed. Wraith Scheme also will automatically clear the flag that shows whether a particular interrupt has been requested, when a requested interrupt has been handled. You yourself must set the flags indicating that an interrupt is requested. Further discussion of the set-and-clear mechanism for interrupt-requested flags follows shortly.

Wraith Scheme will create that file for you at path "Library/Application Support/WraithScheme/Wraith Scheme Interrupt Information" relative to your home directory. Thus in my own file system, the full path to that file is "/Users/JayFreeman/Library/Application Support/WraithScheme/Wraith Scheme Interrupt Information", but in your file system, your user name will appear instead of "JayFreeman".

To save typing, Wraith Scheme provides a procedure that you may call to obtain the full path to that file. See the discussion of procedure "e::interrupt-file-information", below. Note that you may have to add extra double-quotes to the string returned, because of the embedded blank spaces, depending on what you want to do with the path.

Here is a C++ typedef for the structure of that file, and some #defines for the constants in it that are valid as I write these words. The file contains exactly one instance of this struct, aligned starting at offset zero in the file.

    #define ABSOLUTE_MAX_KITTENS 32
    #define NUMBER_OF_INTERRUPTS 1024
    
    typedef struct interruptInformationFileContent_struct {
    
        volatile int KittenPID[ ABSOLUTE_MAX_KITTENS ];   // 32-bit int!!
        volatile char interruptRequested[ NUMBER_OF_INTERRUPTS ];
        volatile char interruptInstalled[ NUMBER_OF_INTERRUPTS ];
    
        } interruptInformationFileContent;
    
    

Note one possible "gotcha" in using that typedef. The array "KittenPID" is an array of 32-bit integers. Depending on your convention and personal preference, you may have to change the declaration of the array depending on whether you use the typedef in 32-bit or 64-bit code.

Each Wraith Scheme process memory-maps that same piece of shared memory into its own address space as well. Thus, Wraith Scheme can look through the table to find which interrupt or interrupts are pending. There is an implicit prioritization of interrupts, as well: Whenever a Wraith Scheme process responds to SIGUSR1, it will look through the interrupt flags in numerical order, starting with interrupt zero. If you have not specified a handler for some interrupt, nothing happens, but if a handler is specified, Wraith Scheme will arrange for it to run (just what that means is explained in a paragraph or two). Once the handler has been set up to run, Wraith Scheme clears the flag for that interrupt, and proceeds to the next interrupt that has a flag set.

There is also a table of interrupt handlers stored in Wraith Scheme main memory, indexed by interrupt number. An empty list corresponds to "no handler". A valid non-empty table entry is a cons cell whose car is a kitten number -- the number of the kitten that is supposed to execute the handler -- and whose cdr is an S-expression to be added to the input queue for that kitten. I refer to this S-expression as the "handler" for the interrupt. It can be anything you like -- just a number or a string might be enough to demonstrate that an interrupt is working (it will eventually appear in the Wraith Scheme window of the kitten in question) -- but what will presumably be most useful will be a quoted procedure application, something like

    '(do-something-wonderful foo bar baz)
    

that the kitten in question is supposed to execute.

You have the option of using the table in the interrupt information file, that shows whether interrupt handlers are installed, to decide whether or not to request an interrupt, or to present an error message if some other part of your code has neglected to install a necessary interrupt handler. Wraith Scheme will in any case not attempt to handle an interrupt for which no handler has been installed. If you inadvertently request an interrupt for which no handler has been installed, Wraith Scheme will not present an error message, but it will clear the interrupt-requested flag for that interrupt.

Be careful not to confuse the table of interrupt handlers with the table of booleans showing whether particular handlers have been installed. The former is a data structure in Wraith Scheme main memory; the latter is a data structure in the publicly-known binary file describing the state of the Wraith Scheme interrupt system.

Finally, Wraith Scheme has a critical-section blocking mechanism to make sure that even if two separate Wraith Scheme processes should receive SIGUSR1 at the same time, no pending interrupt will run more than once.

Let's summarize: When a Wraith Scheme MomCat or kitten processes a SIGUSR1 interrupt for which you have defined a handler, it will end up adding an S-expression of some kind to the input queue of some Wraith Scheme process. The process whose input queue gets the S-expression may or may not be the process that received the SIGUSR1. You can send the SIGUSR1 interrupt to any Wraith Scheme process you like, but the kitten to whose queue the S-expression gets added will always be the kitten whose kitten number is the car of the entry for that interrupt in the table of interrupt handlers.

Note that the same table of interrupt-pending flags is shared by all Wraith Scheme processes, as is the same table of interrupt handlers and the same table indicating whether handlers have been installed. Therefore it does not matter which Wraith Scheme process receives a SIGUSR1; any one can deal equally well with any interrupt. Notwithstanding, the response time for handling an interrupt will necessarily depend on how busy the Wraith Scheme process was when it received the SIGUSR1 signal.

The procedures that deal with interrupts are:

    (c::interrupt-handler-vector)
    

      Return the entire vector of 1024 interrupt handlers. It is probably not a good idea to change interrupt handlers by modifying this vector directly; Wraith Scheme is quite likely to crash if the format of the vector entries is not exactly right: Use the following procedures instead.

    (c::set-interrupt-handler! <interrupt-number> <kitten-number> <S-expression>)
    

      Set an entry in Wraith Scheme's table of interrupt handlers to make the given interrupt be handled by means of the given kitten evaluating the given S-expression.

    (c::clear-interrupt-handler! <interrupt-number>)
    

      Make Wraith Scheme ignore the given interrupt, by setting its entry in Wraith Scheme's table of interrupt handlers to the empty list.

    (c::clear-interrupt-requested! <interrupt-number>)
    

      Clears the interrupt-requested flag for the given interrupt, which may be useful for debugging or error-handling if it has accidentally been set.

    (c::interrupt <kitten-number> <interrupt-number>)
    

      Turn on the interrupt flag for the given interrupt, and send SIGUSR1 to the given kitten: Wraith Scheme thereby sends an interrupt to itself.

    (e::interrupt-file-information)
    

      This procedure provides easy access to the name and size of the public file that contains the Wraith Scheme interrupt flags and the list of Wraith Scheme Unix process identifiers. It returns a multiple-values return whose first element is a string giving the full absolute path to the file, and whose second element is an integer indicating its size in bytes.


Precautions for Using Interrupts:

The Wraith Scheme interrupt system has a great deal of power and flexibility, but also has some important limitations. Notably, there is no queue of pending interrupts. Therefore:

  • Processes that signal SIGUSR1 to Wraith Scheme should use an atomic operation to set the interrupt flag for the interrupt handler they wish to use, and should not send SIGUSR1 until they have gotten the flag for themselves. This mechanism will ensure that no process sends a second interrupt of a given number before the first such has been handled. Recall that the Wraith Scheme process that receives an interrupt will clear the interrupt flag after it has added the handler to the input queue of the kitten that is supposed to receive it.

  • Alternatively, if you arrange that only one process ever requests a particular interrupt, that process might wait on some observable event, such as a write by Wraith Scheme to shared memory-mapped memory, to tell that the handler for that interrupt has run. This mechanism will ensure that no one process sends interrupts too fast, but will not work if more than one process is capable of sending the same interrupt.

  • There is no guarantee that an interrupt will be handled "soon" after Wraith Scheme has received a SIGUSR1 or SIGALRM signal. In particular, interrupts will not be handled while garbage collection is occurring.


Demonstrating the Foreign-Function Interface in Wraith Scheme:

The foreign-function interface is set up so that it is possible to demonstrate how it works just by having different Wraith Scheme processes use it to share information and to interrupt each other. That will at least let you put off the scary task of writing actual memory-mapping code on your own, for a while. Here is how to do a short demonstration.

First, get Wraith Scheme going with at least two kittens besides the MomCat. In one kitten, enter the following Scheme code:

    (define a (e::make-memory-mapped-block 1000 "/tmp/foo"))
    (e::memory-mapped-block? a)
    (e::poke8 a 0 1)
    (e::poke16 a 2 2)
    (e::poke32 a 4 3)
    (e::poke64 a 8 4)
    

What that does is define a memory-mapped block of size 1000 bytes, associated with file "/tmp/foo", double-check that it has been created (with the test predicate, "e::memory-mapped-block?", and then poke some data into it, at offsets 0, 2, 4, and 8.

Next, go to some other kitten, and let's see about accessing that data from there. If you just try to do it right away, you will get an error:

    (e::peek8 a 0)
    
    Problem: Memory-mapped block has not yet been mapped for this kitten -- cannot read value.  (Resetting)
    Top-level loop ...
    

because you have not yet told that kitten to do any memory-mapping. What you must first do is

    (e::memory-map-block-in-current-kitten a)
    

which will use the information stored in the Scheme "memory-mapped block" object "a" to map the same block in the current kitten. Then you can read the data:

    (e::peek8 a 0)
    1
    (e::peek16 a 2)
    2
    (e::peek32 a 4)
    3
    (e::peek64 a 8)
    4
    

What about interrupts? Let's put in a simple handler for interrupt 19, that will run on kitten 1.

    (c::set-interrupt-handler! 19 1 '(display "Hello, world\n"))
    

You can run that code in any kitten you like. Now, let's go to kitten 2, and from there interrupt kitten 0, and cause it to respond to interrupt 19. In kitten 2's input, type this:

    (c::interrupt 0 19)
    

There will be nothing showing in kitten 0's window to show that it has responded to the interrupt, but if you look in the window for kitten 1 you will see

    Hello, world
    #t
    

which was not there before. The interrupt handler has run.

Remember to delete the shared-memory file, "/tmp/foo", when you are done with it. Wraith Scheme can't very well do that for you automatically, because it has no idea what other processes may be using the file and when they will be done with it.


Demonstrating the Foreign-Function Interface with Other Programs:

Get Wraith Scheme going and look in the "Source Code Examples" submenu of the "Help" menu. There are several C++ programs there that are designed to illustrate the Wraith Scheme foreign-function interface. Open the one labeled "Memory Map C++ Demo".

Copy that code to a file somewhere, then compile it from a Unix (Terminal) command line, with the command that is shown in the program text itself. (To do so may require that you have Apple's "Xcode" development environment installed on your Macintosh, or that you have installed various Unix utilities by other means. It works for me with Xcode installed on my Mac Pro.) Don't run it yet.

Once that code is ready to run, cut and paste the following Scheme code into Wraith Scheme. I have added comments in-line in the code block to explain what is happening.

    ;;  The first line creates a memory-mapped block that
    ;;  is associated with the file "/tmp/foo" -- the same
    ;;  file that the compiled C++ program is going to use.
    
    (define a (e::make-memory-mapped-block 1000 "/tmp/foo"))
    
    ;;  Now, let's put some data in the file.
    
    (e::poke8  a 0 1)
    (e::poke16 a 2 2)
    (e::poke32 a 4 3)
    (e::poke64 a 8 4)
    
    ;;  Let's check to be sure it is there --
    ;;  These commands should return 1, 2, 3 and
    ;;  4, respectively.
    
    (e::peek8  a 0)
    (e::peek16 a 2)
    (e::peek32 a 4)
    (e::peek64 a 8)
    
    ;;  But notice that when we look 16 bytes further
    ;;  on in the memory-mapped block, there is nothing
    ;;  there but zeros.
    
    (e::peek8  a 16)
    (e::peek16 a 18)
    (e::peek32 a 20)
    (e::peek64 a 24)
    
    ;;  Now let's run that C++ program.  You could run it from
    ;;  the Unix command line as well.  All the C++ program
    ;;  does is copy the data we put into the memory-mapped
    ;;  block by using Scheme, into new locations 16 bytes
    ;;  further on.  Any output from the program goes into a
    ;;  log file, so you can see what happened if there were
    ;;  errors.
    
    (e::system "/tmp/MMapDemo >& /tmp/MMapDemo.log &")
    
    ;;  However you are running the program, wait a second
    ;;  or two to be sure it has had time to load and run.
    
    ;;  Now, let's look and see if the data have been
    ;;  copied.  If all goes well, these next commands should
    ;;  return, respectively, 1, 2, 3 and 4.
    
    (e::peek8 a 16)
    (e::peek16 a 18)
    (e::peek32 a 20)
    (e::peek64 a 24)
    

The Unix/C++ "mmap" function is a bit tricky to use, and I find it rather poorly documented, but this example should get you started on using it to share data between Wraith Scheme and other programs.


Now let's try an example that uses interrupts to tell Wraith Scheme when the data have been copied. This is a whole new program that is closely similar to the preceding one.

Get Wraith Scheme going and look in the "Source Code Examples" submenu of the "Help" menu. There are several C++ programs there that are designed to illustrate the Wraith Scheme foreign-function interface. Open the one labeled "Memory Map / Interrupt C++ Demo".

Copy that code to a file somewhere, then compile it from a Unix (Terminal) command line, with the command that is shown in the program text itself. (To do so may require that you have Apple's "Xcode" development environment installed on your Macintosh, or that you have installed various Unix utilities by other means. It works for me with Xcode 3.2 installed on my Mac Pro.) Don't run it yet.

Once that code is ready to run, cut and paste the following Scheme code into Wraith Scheme. I have added comments in-line in the code block to explain what is happening.

    ;;  The first line creates a memory-mapped block that
    ;;  is associated with the file "/tmp/foo" -- the same
    ;;  file that the compiled C++ program is going to use.
    
    (define a (e::make-memory-mapped-block 1000 "/tmp/foo"))
    
    ;;  Now, let's put some data in the file.
    
    (e::poke8  a 0 1)
    (e::poke16 a 2 2)
    (e::poke32 a 4 3)
    (e::poke64 a 8 4)
    
    ;;  Let's check to be sure it is there --
    ;;  These commands should return 1, 2, 3 and
    ;;  4, respectively.
    
    (e::peek8  a 0)
    (e::peek16 a 2)
    (e::peek32 a 4)
    (e::peek64 a 8)
    
    ;;  But notice that when we look 16 bytes further
    ;;  on in the memory-mapped block, there is nothing
    ;;  there but zeros.
    
    (e::peek8  a 16)
    (e::peek16 a 18)
    (e::peek32 a 20)
    (e::peek64 a 24)
    
    ;;  Next, we need an interrupt handler.
    
    (c::set-interrupt-handler! 42 0
      '(begin (display (list (e::peek8  a 16)
                             (e::peek16 a 18)
                             (e::peek32 a 20)
                             (e::peek64 a 24)))
              (newline)))
    
    ;;  Now let's run that C++ program.  You could run it from
    ;;  the Unix command line as well.  What the C++ program
    ;;  does is first copy the data we put into the memory-mapped
    ;;  block by using Scheme, into new locations 16 bytes
    ;;  further on.  Any output from the program goes into a
    ;;  log file, so you can see what happened if there were
    ;;  errors.  Then it waits five seconds and sends an
    ;;  interrupt -- number 42 -- to the MomCat, which will 
    ;;  read the copied values from shared memory and print
    ;;  them out.
    
    (e::system "/tmp/MMapInterruptDemo >& /tmp/MMapInterruptDemo.log &")
    


Two other programs demonstrate the use of the Wraith Scheme foreign-function interface with Unix Sockets.

Get Wraith Scheme going and look in the "Source Code Examples" submenu of the "Help" menu. There are several C++ programs there that are designed to illustrate the Wraith Scheme foreign-function interface. Open the ones labeled "Socket C++ Demo -- Client Part" and "Socket C++ Demo -- Server Part".

Copy those source listings to files somewhere, then compile them from a Unix (Terminal) command line, with the commands that are shown in the program text itself. (To do so may require that you have Apple's "Xcode" development environment installed on your Macintosh, or that you have installed various Unix utilities by other means. It works for me with Xcode 3.2 installed on my Mac Pro.) Don't run them yet.

Once those two programs are ready to run, start Wraith Scheme. You don't need any extra kittens for the demo, but more than one is perhaps more fun. Enter the following Scheme code (in any kitten).

    (define server-mmapped-block (e::make-memory-mapped-block 1000 "/tmp/serverMMap"))
    (c::set-interrupt-handler! 42 0
      '(begin (display (e::peek-c-string server-mmapped-block 0)) (newline)))
    (define client-mmapped-block (e::make-memory-mapped-block 1000 "/tmp/clientMMap"))
    

Next, if you did not type the previous Scheme commands in the MomCat, go to the MomCat's window and type

    (e::memory-map-block-in-current-kitten server-mmapped-block)
    

You must do that because the interrupt handler you have set up is going to have the MomCat read from that memory-mapped block.

Now, in a Terminal shell window, start the "SocketServer" program:

    > SocketServer
    Making server socket:
    Binding...
    Starting to listen with a queue of 100 elements ...
    Server waiting for a connection ...
    

At that point, type control-Z and then "bg", which will keep the "SocketServer" program running in the background, and let you start the "SocketClient" program as well.

    ^Z
    [1]+  Stopped                 SocketServer
    > bg
    [1]+ SocketServer &
    > SocketClient
    Making client socket:
    Client is connecting ...
    Client has a connection, client Unix process identification is <whatever>
    Server has a connection.
    ################################ Message 0 ################################
    

Note that the Unix process identification of the client is provided by the Terminal-shell message from the client that begins "Client has a connection...".

Now go back to Wraith Scheme, and in the kitten where you entered the first batch of Scheme code, type what follows, being careful to change the second line of code so that it contains the actual process number of the client:

    (e::poke-c-string client-mmapped-block 0 "Your favorite string.")
    (e::system "kill -2 <Unix process identification of client>")
    

In the MomCat's Wraith Scheme Window, you will find that the interrupt handler has typed

    Your favorite string.
    

You can repeat the "e::poke-c-string" and "e::system" commands as many times you like, with whatever strings you like. The Terminal shell window will provide a description of what is going on like the following, in which I mindlessly repeated the command to poke "Your favorite string." several times, and in which the Unix process identification of the MomCat happened to be 2923.

    ################################ Message 0 ################################
    Client wrote "Your favorite string." to server.
    Server read "Your favorite string." from client.
    About to do a system call with "kill -30 2923".
    ################################ Message 1 ################################
    Client wrote "Your favorite string." to server.
    Server read "Your favorite string." from client.
    About to do a system call with "kill -30 2923".
    ################################ Message 2 ################################
    Client wrote "Your favorite string." to server.
    Server read "Your favorite string." from client.
    About to do a system call with "kill -30 2923".
    ################################ Message 3 ################################
    Client wrote "Your favorite string." to server.
    Server read "Your favorite string." from client.
    About to do a system call with "kill -30 2923".
    ################################ Message 4 ################################
    Client wrote "Your favorite string." to server.
    Server read "Your favorite string." from client.
    About to do a system call with "kill -30 2923".
    ################################ Message 5 ################################
    


Foreign-Function Interface Recommendations and Reminders:

I might recommend the following general principles for using the Wraith Scheme foreign-function interface.

  • Think of the interface as "coarse-grained": It is probably more trouble than it is worth to use it for lots of interactions with simple functions that don't do much.

  • Be cautious about allocating too much shared memory by memory-mapping. Wraith Scheme, and any associated processes, will slow down dramatically -- "swap to death", as they say -- as the total amount of memory used becomes similar in size to the total amount of physical memory (RAM) you have installed in your computer.

  • Any Wraith Scheme process can and will respond to any interrupt immediately, but ...

  • ... there is no way to guarantee that the interrupt will be handled immediately: If the kitten that is supposed to handle the interrupt is busy -- evaluating something -- then the interrupt will not be handled until that kitten is free; that is, idle in its top-level loop. Furthermore ...

  • ... interrupt-handling is postponed during garbage collection.

  • Wraith Scheme has no mechanism for remembering that any given interrupt has been requested more than once. If an interrupt flag is set, Wraith Scheme will arrange to run that interrupt's handler once, and only once, and will then clear the flag. (The flag is cleared after the interrupt handler has been added to the intended kitten's input queue, which is probably before the interrupt handler actually runs.) If you want that handler to run again, you must reset the flag and send another SIGUSR1 interrupt.

  • The recommended way for any process to set the interrupt flag for any given interrupt is to loop on some form of atomic test-and-set for bit zero of the flag in question, until that function returns true.

  • A perfectly satisfactory way to send SIGUSR1 to a Wraith Scheme process is:

      system("kill -30 <a Wraith Scheme process identifier>");
      

    You can get the process identifier of any Wraith Scheme kitten by using the <interrupt info> pointer mentioned above, like this:

      <kitten process identifier> = <interrupt info>->KittenPID[ kittenNumber];
      

    See the demonstration programs a few sections above for more details on how to use the interrupt information data structure.

  • Don't send the SIGUSR1 signal until after you have set the flag for the interrupt you want.

  • If you need to know when an interrupt handler has actually run, arrange that the handler tell you, perhaps by setting a flag of its own somewhere in a shared memory block you have set up for use in the foreign-function interface.

  • If you are running more than one non-Wraith-Scheme process that needs to communicate with Wraith Scheme, it might be best if you set up each such process to use its own separate set of interrupts.

  • For prompt interrupt handling, arrange that your interrupt handlers get sent to a kitten whose sole job is to handle interrupts. Don't run any other code in that kitten, because the kitten will not get around to handling interrupts (that is, to reading its input queue) until any other code that it is evaluating is done. If you anticipate lots of use of several different interrupts, it might be wise to have separate kittens dedicated to handling each one.


Forgettable Objects:

Wraith Scheme has an enhancement that allows creation and use of data structures that are set up automatically to be forgotten -- purged from Scheme main memory -- after some time has passed, even if they are not garbage in the formal sense of the word. The interface to forgettable objects lets you ask whether an object still exists before trying to use it, and provides you with knowledge and control of the precise details of when and if it disappears.

So what good is that?

As with most of the enhancements to Wraith Scheme, I put this one in because I thought I might use it in personal projects. I was intending to provide a facility usable by artificial intelligence projects to mimic the "short-term memory" of biological minds. Stored data can go away, and instead of having a program crash when it tries to use an invalid reference, the interface provides a way to tell that "I forgot", which the program can then deal with, perhaps by rebuilding a data structure or by going out and making a new, current, observation of relevant real-world data. (That is sort of like what you and I do when we forget things, though perhaps without the whining.)

Note the difference in behavior from standard Lisp garbage collection of unreferenced items. With forgettable objects, a program can have what it thinks is a valid reference to something, and it won't find out that the reference no longer works till it tries to use it.

But wait, there's more! I tried to create a mechanism that would also allow several other useful behaviors related to forgetting things, such as:

  • Caches in which an object is guaranteed to be remembered for a certain time, and then will be forgotten when nothing else has a reference to it.

  • Use-count mechanisms for deciding what to do with an object based on how much it has been used. (The forgettable-object interface provides primitives for creating such a mechanism.)

  • Time-of-last-reference mechanisms for deciding what to do with an object based on when it was last used. (The forgettable-object interface provides primitives for creating such a mechanism.)

  • Means for a user to prepare in advance to delete large, infrequently-used data structures from Scheme memory, at times when memory is getting tight or garbage collection is taking too long.


Forgettable Object Content:

A forgettable object is in essence a new kind of Scheme object, which is a wrapper around any Scheme object and two additional items:

  • An expiration time, after which the object might get forgotten (details shortly).

      Technical Note: Expiration times are "Unix times" -- unsigned integers which are interpreted as seconds since the beginning of calendar year 1970 (common era). In Apple Macintosh 64-bit applications, such as Wraith Scheme, these integers are 64-bit, which means that Wraith Scheme's mechanisms for dealing with time will cease to operate correctly approximately 3 000 000 000 000 years from now.

      If you are still using Wraith Scheme then, and you would like a better mechanism to deal with time, send me some EMail and I will see what I can do ...

  • A probability of remembering -- a number in the range [ 0, 1 ] -- which affects what happens at the object's expiration time.

      Technical Note: Wraith Scheme's particular implementation of forgettable objects actually involves three new kinds of Scheme objects: forgettable-unexpired, forgettable-forgotten, and forgettable-remembered.


Forgetting Behavior:

  • The idea here is that before its expiration time, a forgettable object has not forgotten its content, and can return it with no problem when asked for it. However, at the first full garbage-collection after its expiration time, Wraith Scheme makes a decision based on the probability of remembering, and on whether the content of the forgettable object is remembered anywhere else in Wraith Scheme, of whether the forgettable object shall forget its content (permanently), or remember it (again, permanently).

    When a forgettable object is asked for its content, via procedure "e::forgettable-object", it returns two values. The first value is a boolean which is true if, and only if, its content is remembered. The second value depends on the first: If the object's content is remembered, the second value is that content. If not, the second value is #t.

    A forgettable object cannot forget its content if that content is bound to something else in Wraith Scheme; that is, if the content is otherwise not garbage. The connection between a forgettable object and its content is something like what other programming languages call a "weak reference", but be careful, that term means different things to different people.

    Garbage collection treats forgettable objects reasonably: A forgettable object is not garbage as long as any Wraith Scheme process has a way of accessing it in Scheme memory. The content of a forgettable object is not garbage as long as at least one of two conditions obtains: (1) The forgettable object itself is not garbage, or (2) any Wraith Scheme process has a way -- a chain of pointers -- of accessing that content in Scheme memory, other than through the forgettable object itself.

  • If a forgettable object has a probability of remembering of one, then at the first garbage collection after expiration time and after the content of the forgettable object has otherwise become garbage, the forgettable object will permanently remember its content.

  • If a forgettable object has a probability of remembering of zero, then at the first garbage collection after expiration time and after the content of the forgettable object has otherwise become garbage, the forgettable object will permanently forget its content, and that content -- now completely garbage -- will be garbage-collected.

  • If a forgettable object has a probability of remembering between zero and one, then at the first garbage collection after expiration time and after the content of the forgettable object has otherwise become garbage, the forgettable object will choose randomly, based on the probability, whether or not permanently to remember its content. Forgotten content will garbage-collected.

    For example, a probability of remembering of 0.75 corresponds to a 75 percent chance of being remembered, and a 25 percent chance of being forgotten.


Programming Interface to Forgettable Objects:

    (e::make-forgettable <given object> <a Unix time> <probability of remembering> )
    

      Create and return an unexpired forgettable object containing the given object, with the given Unix time as its expiration time, and with the given probability of remembering.

    (e::forgettable? <object>)
    

      Is the given object a forgettable object?

    (e::forgettable-forgotten? <object>)
    

      Is the given object a forgettable object that has been forgotten?

    (e::forgettable-remembered? <object>)
    

      Is the given object a forgettable object that has been remembered?

    (e::forgettable-unexpired? <object>)
    

      Is the given object a forgettable object that is neither forgotten nor remembered?

    (e::forgettable-object <forgettable-object>)
    

      Returns multiple values: The first is #t if the forgettable object has not been forgotten and #f if it has been forgotten; the second is the stored object if the forgettable object has not been forgotten and is undefined if that object has been forgotten.

    (e::forgettable-expiration-time <forgettable-object>)
    

      Returns multiple values: The first is #t if the forgettable object has not been forgotten and #f if it has been forgotten; the second is the expiration time, which is still remembered by the forgettable object even if the object stored within it has itself been forgotten.

    (e::forgettable-remember-probability <forgettable-object>)
    

      Returns multiple values: The first is #t if the forgettable object has not been forgotten and #f if it has been forgotten; the second is the probability of remembering if the forgettable object has not been forgotten and is undefined if that object has been forgotten.

    (e::set-forgettable-expiration-time! <forgettable-object> <a Unix time>)
    

      Change the expiration time of the forgettable object to the new value provided. This operation will succeed if, and only if, the forgettable object is unexpired. Return #t if the operation was successful and return #f if not.

    (e::set-forgettable-remember-probability! <forgettable-object> <probability>)
    

      Change the probability of remembering of the given forgettable object to the new value provided. This operation will succeed if, and only if, the forgettable object is unexpired. Return #t if the operation was successful and return #f if not.

    e::entropy-death
    

      This symbol is bound to the largest value that Wraith Scheme can store as a signed fixnum; if taken as a Unix time, it corresponds to approximately 3 000 000 000 000 years in the future.

        Technical Note: It is presumptuous of me to label an epoch so near with a symbol that suggests that the Universe will have achieved entropy death by then. I beg forgiveness from inhabitants of solar systems whose suns are faint red dwarves.


Some Uses of Forgettable Objects:

  • A forgettable object whose expiration date is in the future and whose probability of remembering is zero is cached until that time, and for as long after as anyone is using the object.

  • To use other mechanisms of "expiring" objects, wrap "e::forgettable-object" inside a specifically written accessor, which might keep track of a use count or time of last use, and might change the object's expiration time to one in the past when it decides to allow the object to expire.


Kitten Graphics:

Wraith Scheme's "Kitten Graphics" system is in essence an implementation of the "Turtle Graphics" drawing system that Seymour Papert created for the Logo programming language in the late 1960s. I renamed it to fit Wraith Scheme's overall feline theme. The general idea of turtle graphics is that some kind of virtual creature moves around the drawing area carrying a pen to draw with. It responds to program commands to go forward, turn left or right, press the pen down so as to make a line, pick the pen up, and so on, and thereby draws a picture.

In the original system, the virtual creature was not virtual: It was an actual robot that crawled around a piece of paper taped to the floor. In Wraith Scheme Kitten Graphics, the virtual creature is a kitty pushing a ball of yarn.

The system draws in a special window, the Wraith Scheme Kitten Graphics window. That window is closed when Wraith Scheme starts running. You may open it by checking the "Show Kitten Graphics Window" menu item in the Wraith Scheme Window Menu. That window looks like the following image:

Kitten Graphics Window As Opened

The Kitten Graphics Window.

The kitty is in her "home" position, in the center of the drawing area of the window and facing upward, ready to start drawing. Her home position is always in the center of the drawing area, even if you have resized the window.

In the image shown, the kitty is pushing a ball of black yarn. Thus the next line to be drawn will be black.

There is a way to make the kitty invisible, so you can see the entire drawing without the kitty getting in the way -- I will describe how to do that shortly. There is another point to remember about kitty visibility: Drawing goes much faster when the kitty is invisible -- it takes the Macintosh a good deal of time to draw her image. On the other hand, drawing is much cuter when the kitty is in sight!

Drawing takes place in the square area that occupies most of the window. The background color of that area starts out the same as the background color of the text areas of the Wraith Scheme main window, but that is mostly so you can tell the different Kitten Graphics windows apart if you have more than one Wraith Scheme kitten running. You can reset the background color by using the color well that is at the right center of the window.

  • The "Erase Drawing" button clears the drawing area -- any drawing that you have made will be gone for good.

  • The color well labeled "Window Color" is used to change the background color of the drawing area of the window. Touch it to use it.

  • The "Hide Window" button makes the Kitten Graphics Window disappear -- you will have to use the menu item to make it reappear.

  • The "Hide Kitty" button makes the kitty disappear. At that time the title of the button will change to "Show Kitty", and pushing the button again will make the kitty reappear.

    The kitty's ability to draw has nothing to do with whether she is visible or not: Hiding the kitty just gets her out of the way so you can see the entire drawing, and also speeds up the drawing process itself.

  • The "Kitty Come Home" button is useful when the kitty has escaped from the drawing area and is no longer visible in the window. Pushing that button restores the kitty to her home position. If the kitty is moving under program control when you push the "Kitty Come Home" button, she will keep moving, and might possibly scamper off the drawing area again. To make the kitty stop moving, you must reset Wraith Scheme to top level.

    When you push the "Kitty Come Home" button, no line will be drawn while the kitty is returning to her home position.

  • When the kitten graphics system starts up, the kitty is visible, the kitty is in her home position, the pen is down (that is, the kitty is set to draw lines), the line color is black, and the line width is one point (points are Macintosh screen coordinate units).

I will give a detailed description of the Wraith Scheme procedures for drawing shortly, but before I go into detail let's give a simple example. Let's draw a square. The first Scheme procedure we use is "e::kitty-forward". We will go forward 100 points -- a "point" is the Macintosh unit for measuring distance on the computer screen. The command is:

    (e::kitty-forward 100)
    

The result is:

Kitten Graphics Square 1

The first line of a square.


Now let's turn left 90 degrees:

    (e::kitty-left 90)
    

Kitten Graphics Square 2

After turning left.


Go forward 100 more and turn left again:

    (e::kitty-forward 100)
    (e::kitty-left 90)
    

Kitten Graphics Square 3

Two sides of a square.


Finish the square:

    (e::kitty-forward 100)
    (e::kitty-left 90)
    (e::kitty-forward 100)
    

Kitten Graphics Square 4

The completed square..


How about changing the background color to pale blue and hiding the kitty:

Kitten Graphics Square 5

Tidied up.


I think that should have given you a good introduction to how this graphical system works ... and it does get fancier ...

Kitten Graphics Fancy


The procedures that deal with Kitten Graphics all start with "e::kitty-". They are:

    (e::kitty-color)
    

      Returns the number corresponding to the pen color in use.

    (e::kitty-color? <object>)
    

      Returns whether or not the argument is a number that may represent a kitty pen color; that is, whether or not the argument is an exact integer in the range [0..9].

    (e::kitty-come-home)
    

      Brings the kitty back to her home position and orientation, which is in the middle of the drawing area of the Wraith Scheme Kitten Graphics Window, facing upward.

      This operation may also be performed with the "Kitty Come Home" button, located near the lower right corner of the Wraith Scheme Kitten Graphics Window.

      If you perform this operation by pushing the button, and the kitty is in motion when you do so, the motion will not stop: It is possible that the kitty will immediately move away from home position. To stop the kitty, you must reset Wraith Scheme to top level.

      This operation works whether the kitty is visible or not, and does not change the visibility of the kitty, only her location.

      The kitty does not draw anything during her journey homeward.

    (e::kitty-erase-drawing)
    

      Erases the entire drawing.

    (e::kitty-forward <real number>)
    

      Moves the kitty forward a distance equal to the number of points (Macintosh screen coordinate units) that is the argument. With an argument that is a negative number, move the kitty backward.

      The kitty will draw a line during this operation if, and only if, the pen is down.

    (e::kitty-heading)
    

      Returns the kitty's heading -- the direction in which she is facing. Headings are measured in degrees, in the range [0 .. 360). A heading of 0 corresponds to the kitty facing straight up. Headings increase clockwise; thus for example, a heading of 90 corresponds to the kitty facing to the right.

    (e::kitty-hide)
    

      Makes the kitty invisible.

      This operation may also be performed with the "Hide Kitty" button, located near the lower right corner of the Wraith Scheme Kitten Graphics Window. That button has the title "Hide Kitty" when the kitty is visible, and "Show Kitty" when the kitty is invisible.

      The kitty's ability to draw has nothing to do with whether she is visible or not. The kitty will draw whenever the pen is down, without regard to her visibility.

    (e::kitty-left <real number>)
    

      Makes the kitty turn left by the number of degrees which is its argument. Negative numbers make the kitty turn right.

    (e::kitty-line-width)
    

      Returns the line width in points (Macintosh screen coordinate units) in use.

    (e::kitty-line-width? <object>)
    

      Returns whether or not the argument is a number that may represent a line width that is, whether or not the argument is an exact real in the range [0..10].

    (e::kitty-pen-down)
    

      Tells the kitty to start drawing. The kitty will continue drawing until advised otherwise, except when she is being brought home by the "e::kitty-come-home" operation.

    (e::kitty-pen-down?)
    

      Returns whether the pen is down; that is, whether the kitty is drawing.

    (e::kitty-pen-up)
    

      Tells the kitty to stop drawing. The kitty will do no further drawing until advised otherwise.

    (e::kitty-right <real number>)
    

      Makes the kitty turn right by the number of degrees which is its argument. Negative numbers make the kitty turn left.

    (e::kitty-set-color <integer>)
    

      Changes the color with which lines will be drawn. This operation does not change whether the pen is up or down, and does not change the color of any lines that have already been drawn.

      Line colors are represented by exact integers, as follows:

      • 0 -- white

      • 1 -- red

      • 2 -- orange

      • 3 -- yellow

      • 4 -- green

      • 5 -- cyan

      • 6 -- magenta

      • 7 -- black

      • 8 -- blue

      • 9 -- purple

      The reason for the rather odd order of colors is so that the same numbers may be used for sense light colors as for line colors, with similar result. Wraith Scheme sense lights predate Wraith Scheme kitten graphics, and only the integers [0..7] are used for sense light colors.

    (e::kitty-set-line-width < real number >)
    

      Changes the width in points (Macintosh screen coordinate units) with which lines will be drawn. This operation does not change whether the pen is up or down, and does not change the width of any lines that have already been drawn. The range of allowed line widths is rather arbitrarily limited to [0..10].

    (e::kitty-show)
    

      Makes the kitty visible.

      This operation may also be performed with the "Show Kitty" button, located near the lower right corner of the Wraith Scheme Kitten Graphics Window. That button has the title "Show Kitty" when the kitty is invisible, and "Hide Kitty" when the kitty is visible.

      The kitty's ability to draw has nothing to do with whether she is visible or not. The kitty will draw whenever the pen is down, without regard to her visibility.

    (e::kitty-x)
    

      Returns the x coordinate of the kitty's position, measured in points (Macintosh screen coordinate units). The x coordinate axis is horizontal. The x coordinate increases to the right, and is zero at the lower left corner of the drawing area of the Wraith Scheme Kitten Graphics Window.

    (e::kitty-y)
    

      Returns the y coordinate of the kitty's position, measured in points (Macintosh screen coordinate units). The y coordinate axis is vertical. The y coordinate increases upward, and is zero at the lower left corner of the drawing area of the Wraith Scheme Kitten Graphics Window.

Here is some sample Scheme code for simple drawings. Cut and paste these procedures into Wraith Scheme and try them out. Note that some of these procedures call others, so you will probably want to load them all. Some of the code is a little simplistic, but it will give you a sense of what kinds of things are possible with kitten graphics.

    
    ;; "circle-right" makes the kitty turn right in an approximation of a
    ;;  circle of radius r -- actually a polygon of n sides.
    
    (define (circle-right r n)
      (letrec ((d (/ (* 2 c::pi r) n))
               (theta (/ 360 n))
               (circle-right-aux
                 (lambda (k)
                    (cond ((<= k 0) #t)
                          (#t (e::kitty-forward d)
                              (e::kitty-right theta)
                              (circle-right-aux (- k 1)))))))
        (e::kitty-forward (/ d -2))
        (circle-right-aux n)
        (e::kitty-forward (/ d 2))
        ))
    
    
    ;; "circle-left" makes the kitty turn left in an approximation of a
    ;;  circle of radius r -- actually a polygon of n sides.
    
    (define (circle-left r n)
      (letrec ((d (/ (* 2 c::pi r) n))
               (theta (/ 360 n))
               (circle-left-aux
                 (lambda (k)
                   (cond ((<= k 0) #t)
                          (#t (e::kitty-forward d)
                              (e::kitty-left theta)
                              (circle-left-aux (- k 1)))))))
        (e::kitty-forward (/ d -2))
        (circle-left-aux n)
        (e::kitty-forward (/ d 2))
        ))
    
    
    ;; "many-circles" draws sixteen circles of radius r that meet
    ;; in a single point and are symmetrically spaced in angle.
    
    (define (many-circles r)
      (let ((one-pair
              (lambda (theta)
                (e::kitty-come-home)
                (e::kitty-right theta)
                (circle-left r r)
                (e::kitty-come-home)
                (e::kitty-right theta)
                (circle-right r r))))
      (for-each one-pair
        (list 0 22.5 45 67.5 90 112.5 135 157.5))))
    
    
    ;; "splat" draws sixteen lines r long radiating
    ;; away from a single point, symmetrically spaced in angle.
    
    (define (splat r)
      (let ((one-line
              (lambda (theta )
                (e::kitty-come-home)
                (e::kitty-right theta)
                (e::kitty-forward r)
                (e::kitty-forward (* r -2)))))
      (for-each one-line
        (list 0 22.5 45 67.5 90 112.5 135 157.5))))
    
    
    ;; "spiral" makes a vaguely spiral figure that has
    ;; n steps whose size increases from the initial value
    ;; of "step"
    
    (define (spiral n step)
      (cond ((> n 0)
              (e::kitty-forward step)
              (e::kitty-right 15)
              (spiral (- n 1) (* step 1.02)))
            (#t #t)))
    
    
    ;; With odd n, "n-point" makes an n-pointed star
    ;; out of lines that are "step" long.
    
    (define (n-point n step)
      (e::kitty-right (/ 360 (* n 4)))
      (letrec ((aux
                 (lambda (k step)
                   (cond ((> k 0)
                            (e::kitty-forward step)
                            (e::kitty-right (- 180 (/ 180 n)))
                            (aux (- k 1) step))
                         (#t #t)))))
              (aux n step)))
    
    
    ;; "color-test" demonstrates the different pen colors.
    
    (define (color-test)
      (define (test-color n)
        (e::kitty-set-color n)
        (e::kitty-forward 30)
        (e::kitty-right 36)
        (display (e::kitty-color))
        (newline))
      (for-each test-color '(0 1 2 3 4 5 6 7 8 9))
      #t)
    
    
    ;; "up-and-down" demonstrates the commands to
    ;; raise and lower the pen.
    
    (define (up-and-down)
      (e::kitty-forward 10)
      (e::kitty-pen-up)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-down)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-up)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-down)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-up)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-down)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-up)
      (display (e::kitty-pen-down?))
      (newline)
      (e::kitty-forward 10)
      (e::kitty-pen-down)
      (display (e::kitty-pen-down?))
      (newline)
      )
    
    
    ;; "position-test" sends the kitty to a series of known positions
    ;; and prints them out in the Wraith Scheme Main Window.
    
    (define (position-test)
      (display (list (e::kitty-x) (e::kitty-y) (e::kitty-heading)))
      (newline)
      (e::kitty-forward 30)
      (e::kitty-right 90)
      (display (list (e::kitty-x) (e::kitty-y) (e::kitty-heading)))
      (newline)
      (e::kitty-forward 30)
      (e::kitty-right 90)
      (display (list (e::kitty-x) (e::kitty-y) (e::kitty-heading)))
      (newline)
      (e::kitty-forward 30)
      (e::kitty-right 90)
      (display (list (e::kitty-x) (e::kitty-y) (e::kitty-heading)))
      (newline)
      (e::kitty-forward 30)
      (e::kitty-right 90)
      (display (list (e::kitty-x) (e::kitty-y) (e::kitty-heading)))
      (newline)
      )
     
    
    ;; "fancy-colors" displays a lot of colored circles ...
    
    (define (fancy-colors)
      (e::kitty-set-color 1)
      (many-circles 15)
      (e::kitty-set-color 4)
      (many-circles 30)
      (e::kitty-set-color 8)
      (many-circles 45)
      (e::kitty-set-color 3)
      (many-circles 60)
      (e::kitty-set-color 5)
      (many-circles 75)
      (e::kitty-set-color 6)
      (many-circles 90)
      (e::kitty-set-color 7)
      )
    


Kittens -- Parallel Processing in Wraith Scheme:

To study independence, keep a cat; to study diplomacy, keep two.

The Wraith Scheme enhancements for parallel processing are new and novel, and their development was fraught with bugs. It is therefore most important to note that you do not have to use them. Indeed, Wraith Scheme's default preferences are set up so that Wraith Scheme will run as a single process until and unless you request otherwise.


Introduction to Wraith Scheme Parallel Processing:

  • Wraith Scheme has the ability to run several separate Scheme processes, each with its own Macintosh-style user interface, evaluation engine, and the like, but all sharing the same Scheme memory.

      Technical Note: That's separate processes, not separate threads. Yet confusingly enough, each individual Wraith Scheme process does use several separate threads.

  • There is one privileged process which controls -- or at least supervises -- several aspects of Scheme-memory management that were not easy to run in parallel, most notably including garbage collection. Furthermore, only that privileged process can load or save worlds, modify Preferences, and load startup files, and only the privileged process can automatically load a file when it has been updated.

    Since Wraith Scheme is named after a cat, I decided to continue the feline metaphor. The privileged Scheme process is called the "MomCat", and the others are called "kittens". The image of a mother cat trying to ride herd on a bunch of rambunctious offspring is perhaps appropriate.

      Non-Technical Note: A friend of mine once claimed to have a fifth-degree black belt in the little known Japanese martial art of ikeneko -- which means "cat arranging". Programmers will be familiar with the skill required. Managers of programmers will be extremely familiar with that skill.

  • At present, the maximum number of separate Scheme processes allowed is thirty-two -- one MomCat, plus up to thirty-one kittens. The number of kittens is determined from the Wraith Scheme Preferences when Wraith Scheme starts up, and the "Preferences" includes a setting for the number of kittens.

    There is no way to add kittens to a group of parallel Wraith Scheme processes that is already running. You may remove a kitten from a group via, e.g., the "Quit" command from that kitten's Wraith Scheme menu, but there is no means to put back a kitten that is gone, or to add a new one if you need more, other than changing the Wraith Scheme Preferences and restarting Wraith Scheme.

    Each kitten has a number that identifies it uniquely. The MomCat also has a kitten number, which is always zero, and the terms "MomCat" and "kitten 0" are synonyms. The other kittens are numbered consecutively from 1.

  • Parallel processing is entirely optional: If you do not want to use it, set the number of kittens to zero by means of the Wraith Scheme Preferences, and Wraith Scheme will run as a single process -- just the MomCat.


Help Menu

Wraith Scheme with four processes running. Note the colored kitten icons at the far right end of the dock.


Background, Philosophy and Intent:

It is probably fair to say that one of the main reasons why I added a parallel processing capability to Wraith Scheme was because I thought it would be a neat thing to do. Notwithstanding, a few additional comments might offer useful guidance if you are contemplating using this capability.

  • To begin with, remember that the capability is real, not simulated: The separate Wraith Scheme user interfaces actually do connect to distinct Unix processes, each of which is a separately running instance of the Wraith Scheme program. On Macintosh computers with more than one processor core, more than one of these processes may be running at the same time.

  • The capability is intended to be a natural match for the degree of hardware parallelism we are likely to see in Macintosh computers during the next few years (but your crystal ball may offer different predictions): When I first wrote these words, in October, 2007, my own Macbook had two processor cores, and the highest-end Mac Pro had eight, so at that time I set up the maximum number of kittens to be fifteen. Times have changed: As I edit this text in 2022, my Mac Pro has twelve cores, each capable of running two threads at a time, and even more powerful Mac Pros are available. The current maximum number of kittens allowed is thirty-one, which -- with the MomCat -- means that you can run thirty-two copies of the Wraith Scheme program at once, all sharing the same Scheme memory. Even more than thirty-two processes might be useful, so perhaps I will increase the maximum kitten number in the future -- the internal program architecture that I use to implement parallel processing will easily scale to several times as many kittens: The only real problem is running out of space for icons in the dock, as you can see in the next image hereafter.

  • The parallelism is thereby probably best-suited to coarse-grained uses: Thirty-two processes is not a lot, and low-end Macintosh computers probably will not get any performance benefits from using that many processes anyway. Folks used to processing arrays on single-instruction, multiple-data machines with tens of thousands of processors, or to searching the web with hundreds of separate machines linked by Ethernet, may find their preferred parallel-programming paradigms inappropriate for Wraith Scheme.

    Plausible uses for parallel Wraith Scheme might include running completely separate programs, running distinct programs which all need to act on a common data structure, or using one Wraith Scheme process to inspect or monitor another.

    Among the hidden virtues of this kind of parallelism are reduction in swapping due to sharing a common Scheme memory, and leveraging the Unix mechanisms to switch and schedule processes instead of having to write your own.

  • The programs run by separate kittens are assumed to be friendly to one another, in the sense that they are all assumed to be written and operated by users who have no interest in in shooting themselves or each other in the foot: Thus although the parallel implementation takes low-level measures to maintain the integrity of its own private, internal data structures, there is nothing like a firewall or a security layer protecting the separate Wraith Scheme processes from one another. So if you want to crash your own program, or overwrite or steal your own data, go right ahead; there is more than enough firepower available to shoot yourself in as many feet as you are likely to possess, and then some.

      Technical Note: By "low-level measures", I mean, for example, that I have attempted to maintain heap integrity by blocking conflicting simultaneous writes.

      Thus, for example, if two or more kittens apply "set!" to the variable "x" at more or less the same time, the value of "x" after all the "set!" operations have returned will be a value that one of the kittens intended, but if you do not impose any additional locking or sequencing mechanisms, there is no guarantee which of the several values of "x" will obtain.

      On the other hand, the storage location referenced by "x" should not end up in an invalid state, such as might occur if different portions of the data structure at that storage location had ended up written by different kittens, who were perhaps attempting to write different kinds of values at more or less the same time.

      Operations like "define", "set-car!", and "set-cdr!" -- indeed, everything that ends with a '!', and some others -- are similarly protected; that is, from the viewpoint of the Wraith Scheme evaluation engine, they are what is called atomic operations.

  • Wraith Scheme provides a number of low-level primitive procedures useful for locking data structures against parallel access and for interprocess communication, but it is up to you to decide what to construct with these building blocks.


Keeping Track of Things:

  • Procedures and variables useful for keeping track of the simpler details of a running group of parallel Wraith Scheme processes include:

     (e::kitten-number) 

      Returns the number of the kitten in which the procedure was called. The MomCat has a kitten number of zero; the kittens, if any, have kitten numbers from one up to and including the value returned by "e::number-of-kittens".

     (c:kitten-reset <kitten-number>) 

      Resets the given kitten: Causes the same effect as the "Reset to Top-Level Loop" command, for the kitten indicated.

    (e::momcat?)
    

      Returns a boolean indicating whether or not it was called in the MomCat.

    (e::number-of-kittens)
    

      Returns the number of kittens that were present when the group of parallel Wraith Scheme processes was started, not counting the MomCat. This number does not change if a kitten exits, as it might if you used its "Quit" command.

      Thus if you start a group of Wraith Scheme processes comprising a MomCat and five kittens, "e::number-of-kittens" returns five, and the kittens are numbered from zero -- the MomCat -- up to and including five.

    (e::show-kittens)
    

      Displays some details about each kitten, in human-readable form.

  • To each top-level loop variable used by the MomCat corresponds a group of variables with similar names and similar functions -- one for each kitten. The names for the top-level loop variables for kitten "N" are constructed by appending "-N" to the corresponding name used in the MomCat. Thus for example, to the MomCat's variable ">+<" corresponds the variables ">+<-1", ">+<-2", ">+<-3", ">+<-4", and so on, in kittens 1, 2, 3, 4, and so on, and similarly for the other six top-level loop variables used by the MomCat.

    I am not going to list the names of all these variables; there are too many.

    Note that you may access any top-level loop variable from any process, not just from the process whose top-level loop binds the variable. Thus for example, if kitten 3 wishes to know what the last expression printed in kitten 5's top-level loop was, it may evaluate ">*<-5" to find out.

  • Each kitten's Wraith Scheme window has a different color, and its icon in the Macintosh dock is colored to match. Furthermore, the title of each Wraith Scheme window shows whether the corresponding process is the MomCat or one of the kittens. The result is colorful, but does tend to clutter up the dock a bit ...

    Kittens

    A proud MomCat with thirty-one colorful kittens.
    Note the colored kitten icons to the right of center in the dock.


  • The Wraith Scheme Window Menu has been enhanced with the "Bring Kitten to Front" menu item: It opens a submenu showing all of the Wraith Scheme processes that are running -- both MomCat and kittens. Choosing an item on this submenu brings the Wraith Scheme window of that process to the front of the display. This menu item is useful when there are many Wraith Scheme processes running at the same time.

    Kittens

    A portion of the Window Menu, showing the submenu for bringing a kitten to the front.


Interprocess Communication:

  • Procedures and other means for interprocess communication include:
    (e::tell-kitten <kitten-number> <any Scheme object>)
    

      This procedure provides access to an input queue mechanism whereby any kitten may provide input for the top-level read-eval-print loop of any kitten, even itself.

      To see how the mechanism works, consider the normal operation of a conventional top-level read-eval-print loop. In psuedocode, and neglecting error-handling and some administrative details, it works like this:

        loop forever {
            read from the keyboard, characters which are
                the text representation of a Scheme object.
            parse the input and create a corresponding actual
                Scheme object -- perhaps an S-expression or
                an atom -- stored in Scheme memory.
            evaluate what you've got.
            print what results.
            }
        
      In the parallel implementation of Wraith Scheme, the idea is to deal with stuff in the input queue before dealing with anything you type at the keyboard. A little more specifically, the psuedocode looks like this:
        loop forever {
            loop forever {
                if there is anything in the input queue {
                    dequeue the item first put in
                    exit the inner loop.
                    }
                if the user has typed any non-whitespace {
                    read from the keyboard, characters which
                        are the text representation of a
                        Scheme object.
                    parse the input and create a corresponding
                        actual Scheme object -- perhaps an
                        S-expression or an atom -- stored in
                        Scheme memory.
                    exit the inner loop.
                    }
                }
            evaluate what you've got.
            print what results.
            }
        

      Note that the contents of the queue are not text strings, but Scheme objects which have already been parsed and evaluated.

      For example:

        (e::tell-kitten 1 '(+ 2 2))
        

      puts into the input queue for kitten one the Scheme object (quote (+ 2 2)). When kitten one gets around to dealing with that queued object, it evaluates it -- to the number "4" -- and prints the text string "4" in its Wraith Scheme window. On the other hand, without the quote, the action is rather different. In the process of the procedure call:

        (e::tell-kitten 1 (+ 2 2))
        

      the argument "(+ 2 2)" is evaluated before the procedure is called. Thus what ends up in kitten one's input queue is the object "4" itself -- the work of evaluation has already been done.

      The message here is that in order to make sure that the kitten addressed does the work, remember to quote the Scheme object that is the second argument of "e::tell-kitten".

      The "e::tell-kitten" procedure is atomic, in the sense that no garbling or missing objects will arise if more than one kitten uses the procedure with the same first argument at the same time; for example, if kitten 1 executes

        (e::tell-kitten 3 '(+ 2 2))
        
      at the same time that kitten 2 executes
        (e::tell-kitten 3 '(+ 4 5))
        
      then kitten three will receive both commands in one order or the other, and will print out both 4 and 9 in its Wraith Scheme window in one order or the other.

      A reset of any kind -- whether because of an error or because of use of the "Reset" command -- clears the input queue of the kitten that was reset.

    (c::kitten-busy? <kitten-number>)
    

      This procedure returns whether the kitten indicated is evaluating or not.

    (c::kitten-input-queue <kitten-number>)
    

      This procedure returns the current content of the input queue of the kitten indicated.

    (c::kitten-empty-queue? <kitten-number>)
    

      This procedure returns a boolean indicating whether the input queue of the kitten indicated is empty.

    (c::kitten-purge-queue <kitten-number>)
    

      This procedure clears the input queue of the kitten indicated.


File System Access:

    Since each Wraith Scheme kitten is a separate Unix process, each kitten maintains its own separate data structures for access to the Unix file system. The mechanisms that Wraith Scheme uses to create and manipulate ports have no way to allow one kitten to have direct access to another kitten's file-system data structures; thus you may only read from or write to a Wraith Scheme port from the kitten that created it.


Locks and Critical Sections:

  • Procedures useful for low-level handling of critical sections in Wraith Scheme include:

    (c::block <Scheme object in main memory>)
    

      Arranges that any attempt to use or modify the object hangs in a loop until the lock is released.

    (c::release-block <locked Scheme object in main memory>)
    

      Releases any lock that may have been established on its argument.

    (e::block-any-binding <pair>)
    

      Arranges that any attempt to use or modify the car or cdr of the pair in a loop until the lock is released, with the exception that modification of the car by means of "c::set-blocked-binding!" is permitted.

    (e::block-symbol-binding <symbol>)
    

      Arranges that any attempt to use or modify the value bound to the symbol hangs in a loop until the lock is released, with the exception that modification by means of "c::set-blocked-binding!" is permitted.

    (c::set-blocked-binding! <lock from e::block-symbol-binding> <object>)
    

      Change the value bound to the (otherwise locked) symbol to the new object given.

    (e::test-and-clear! <pointer to pair containing a fixnum>)
    (e::test-and-set! <pointer to pair containing a fixnum>)
    

      Atomically test and set or test and clear the number contained within the pair, and return the value that obtained previously. That is, "e::test-and-clear!" sets the value of the number to zero, and returns its previous value, while "e::test-and-set!" sets the value of the number to one and returns its previous value.

      These procedures are said to "succeed" if, and only if, they change the value in the pair. Thus, "e::test-and-clear!" succeeds if, and only if, the previous value in the pair was nonzero, while "e::test-and-set!" succeeds if, and only if, the previous value in the pair was zero.

      The procedures are atomic in the sense that if many kittens are trying to use "e::test-and-clear!" on the same pair at the same time, only one will succeed, and similarly for "e::test-and-set!". They are thereby useful for lockless coding in Wraith Scheme, provided that the programmer uses them correctly and consistently, and does not modify the number within the pair by means of other procedures.

    For more detailed examples of the use of these procedures, see their entries in Wraith Scheme Dictionary, which is available via the Wraith Scheme Help menu.


Aids to Debugging:

    Mechanisms useful for debugging parallel Wraith Scheme programs include:

  • Extra kittens:

    It is extremely useful to have at least one more kitten operating than your Scheme program actually needs, to use as a debugging or monitoring window into how things are going. You can use such a kitten -- that is, you can perform Scheme operations in its Wraith Scheme window -- to examine your program, even as it runs: You can check the value of global data, evaluate expressions, and perhaps use other self-monitoring features of your program that you yourself have built in.

  • Top-level loop variables:

    Among the variables you might choose to evaluate in the Wraith Scheme window of an extra kitten might be the top-level loop variables of the kittens that are actually running your program.

  • Procedures:

    (e::show-locks)
    

      Provides a low-level description of every locked object in Scheme main memory, and of the state of locking of bins of the hash tables used to control access to Wraith Scheme's oblist and top-level environment.

      This procedure is deceptive, because other kittens may be setting and releasing locks as it runs: The state of locks displayed may not be current moments later.

    (c::release-locks)
    

      Provides the same display of locked objects that "e::show-locks" does, and then releases every lock that it found.

      This procedure is dangerous and deceptive, for several reasons:

      • Other kittens may be setting and releasing locks as it runs: The state of locks displayed may not be current moments later.

      • Willy-nilly release of locks may cause Wraith Scheme to crash or hang.

      • If an inappropriate lock has made your Scheme program unresponsive, releasing that lock probably will not restore normal behavior; the most it is likely to do is make it easier to investigate the bugs. Thus perhaps there is some important data in your program, that you need to study to determine what the problem is, only you cannot get to it, even to display it, because of a lock. In that case, releasing locks may be profitable, provided that Wraith Scheme doesn't crash or hang.

      An analogy with medical practice may be useful: Releasing locks is unlikely to cure a sick patient, but it may help prepare the cadaver for autopsy, provided that it doesn't blow up the morgue in the process.

    (c::let-deadlock-time-out <a kitten number>)
    

      This procedure provides a way to debug circumstances in which kitten is trying to lock access to a Wraith Scheme object that is already locked, and has remained locked for a worrisome amount of time. The "worrisome amount of time" is guaranteed to be at least 0.01 seconds, but may be longer, depending on the speed of your Macintosh and on what else -- other than Wraith Scheme -- it is doing.

      This procedure does not apply to locks of bins of the hash tables used to control access to Wraith Scheme's oblist and top-level environment.

      If the kitten whose kitten number is passed to the procedure has been trying to obtain access to a locked object for more than the "worrisome amount of time" just described, this procedure will cause it to give up trying, print out some information about what it was attempting to lock, and reset to top-level.

      This procedure may be applied to any kitten and called from any kitten.

      This procedure is dangerous, because release of deadlocks may cause Wraith Scheme to crash or hang.

      Once again, by analogy with medical practice, this procedure is a post-mortem diagnostic tool, not a cure for the patient.


Auxiliary Files:

    The mechanism for sharing memory between a Wraith Scheme MomCat and her kittens requires several auxiliary files. There are seven of them. Six are in the "Wraith Scheme" folder of the "Application Support" folder of the "Library" folder of the user who is running Wraith Scheme. That is, if your user folder should happen to be named "JayFreeman", those files will appear in the folder whose Unix path is

      /Users/JayFreeman/Library/Application Support/Wraith Scheme

    The names of those six files are:

      Wraith Scheme Clowder
      Wraith Scheme Scratch Stack
      Wraith Scheme Space A
      Wraith Scheme Space B
      Wraith Scheme GGC Scratch Space
      Wraith Scheme Interrupt Information

    Do not delete or modify any of these files while Wraith Scheme is running! Doing so will cause Wraith Scheme to crash.

      Technical Note: These files provide a place where the operating system can swap out portions of the shared memory that are temporarily not in use. The first file contains a variety of items used for communication between the various Wraith Scheme processes. (Note that a "clowder" is a colony of cats.) The second file is scratch space where each Wraith Scheme process in turn may dump its Scheme stack during garbage collection. The next two are for Wraith Scheme's two Scheme memories. The next one thereafter is for the use of the generational garbage collector. The last is for sharing information about Wraith Scheme interrupts.

    In normal operation, Wraith Scheme will remove these files when the MomCat exits, for there is then no further need for them. If Wraith Scheme should crash, or otherwise exit abnormally, the files may escape deletion. When Wraith Scheme is not running, you may safely remove any of these files that you find, if you wish. Wraith Scheme will in any case delete any old copies of the files that it finds, the next time it starts up.

    The total size of these six files is somewhat more than twice as large as the amount of memory that Wraith Scheme uses for Scheme memory.

    The seventh auxiliary file is in the "/tmp" directory: It is

      WraithSchemeFatalErrorLog.txt

    This file will only exist if Wraith Scheme needs to write something into it. Delete it if you wish -- Wraith Scheme will recreate it if need be. If you encounter any bugs in Wraith Scheme, and this file seems to contain anything useful in diagnosing them, please include a copy of it with any bug report you send to me.


Optimizations:

    There are a couple of optimization techniques involving Wraith Scheme's parallel processing capability, that you might want to know about.

    First, the locking mechanisms that maintain the integrity of Wraith Scheme main memory when several processes are active, are disabled when you start up Wraith Scheme with just the MomCat. The locking mechanisms are intrinsically slow, so if you only need to run code in one Wraith Scheme window, your Scheme programs will run faster if you start up Wraith Scheme to use only one process.

    Second, items in Wraith Scheme main memory can be read faster when they are being read by the Wraith Scheme process that most recently used them -- that is, the process that has most recently read, written or modified them -- than when they are being read by some other Wraith Scheme process. Thus for example, it makes sense to load files, create top-level data items, and do compilations, in the Wraith Scheme window that will most often use the code and data items thereby created, and it makes sense to perform repeated calculations on a single data structure in a single Wraith Scheme process.

    Third, with an eye toward the day when Non-Uniform Memory-Access (NUMA) computers become more common, Wraith Scheme's garbage-collection mechanism attempts to group items in Wraith Scheme main memory according to which Wraith Scheme process has most recently used them -- that is, the process that has most recently read, written or modified them. The assumption here is that a process that has recently used a particular item is likely to use it again: Thus, improving the localization of items in Wraith Scheme main memory may allow even a simple NUMA page-placement algorithm to preposition data used by a particular process so that it is close (in the NUMA sense) to where that process is executing, and thereby save memory-access time. At any rate, such is my pious hope. (Note, however, that Wraith Scheme will not break apart a cdr-coded list whose various members have been most recently used by different kittens.)

    Fourth, the locking mechanisms that Wraith Scheme uses to protect the integrity of Wraith Scheme main memory are not used for Scheme code which is being run. That is not to say that you cannot modify Scheme code -- lambda bodies, procedure applications, and the like -- for you certainly can, but if one Wraith Scheme kitten attempts to modify code which a different kitten is in the act of running, a crash of one or both kittens is likely.

      Technical Note: That is, the Wraith Scheme continuation is both loaded and read without using such locks.


Logic Programming:

Wraith Scheme provides some support for logic programming in Scheme along the lines of Friedman, Byrd and Kiselyov, 2005). Wraith Scheme does not include any of the fancy software from this work; that's copyrighted, but I hope there are enough primitives to get you going.

  • Wraith Scheme provides two specialized "logic constants", "#u" and "#s", for use in logic programming. You couldn't even get Wraith Scheme to read these identifiers unless I built them in, because R5 identifiers are not generally allowed to start with '#'. So here they are, for anyone who wishes to use them.

    Each of "#u" and "#s" evaluates to itself.

  • Procedures for Logic Programming:

    (e::logic-constant? <object>)
    

      Returns #t if, and only if, its argument is either #u or #s; otherwise, returns #f.

There may be more support for logic programming in future releases of Wraith Scheme.


Non-Printing Objects:

Wraith Scheme provides a specialized non-printing object, "#n", whose printed representation is an empty string, and which furthermore will cause Wraith Scheme's top-level read-eval-print loop to omit printing a newline when #n is the result of the "eval" part of the loop.

"#n" evaluates to itself. To see what "non-printing" means, compare the following examples, in which I have added comments to distinguish what you type at the keyboard from what Wraith Scheme prints at the end of evaluation:

    #t  ;;  You type #t, and then a newline.
    #t  ;;    Wraith Scheme evaluates #t as #t, which it prints, followed by a newline.
    #n  ;;  You type #n, and then a newline, but Wraith Scheme prints nothing.
    
        ;;  In that last example, Wraith Scheme actually evaluated #n to #n, but
        ;;    that result was not displayed, nor was a newline.
    
    (define x 3)   ;;  You type this definition, and then a newline.
    x              ;;    Wraith Scheme prints "x", and then a newline
    x              ;;  You type "x", and then a newline.
    3              ;;    Wraith Scheme prints its value, 3, and then a newline.
    x              ;;  You type "x", and then a newline (again).
    3              ;;    Wraith Scheme prints its value, 3, and then a newline.
    (define y #n)  ;;  You type this definition, and then a newline.
    y              ;;    Wraith Scheme prints "y", and then a newline.
    y              ;;  You type "y" and a newline, and Wraith Scheme prints nothing.
    y              ;;  You type "y" and a newline again, and Wraith Scheme prints nothing.
    

I added "#n" primarily as a value to return from interrupt handlers. I had some interrupts that were called frequently but mostly did nothing; I wanted to have them print nothing at all so as not to clutter up log files with repetitive meaningless output. An interrupt handler has to return something to the top-level read-eval-print loop of the kitten which handles it, so a non-printing object was just the thing to use for a returned value.

Note that you can inspect the non-printing object and get a printable result. Thus in the previous example, the input:

    (e::inspect y)
    

produces the output:

    0000000000000025-0000000000000004: Peculiar Leaf -- Not a Car
    #<Non-printing object>
    


  • Procedures for Non-Printing Objects:

    (e::non-printing-object? <object>)
    

      Returns #t if, and only if, its argument is #n; otherwise, returns #f.

You might consider the 'n' in "#n" to stand for "non-printing" or for "nothing".


Packages:

Many implementations of programming languages provide an implementation of some kind of "package" system, with the dual goals of not cluttering up the namespace and of providing a sane way to deal with situations when two libraries or other modules of code use the same name for different purposes. Wraith Scheme has such a system, but to skirt possible cognitive dissonance, I have consciously avoided such common names as "use", "require", "import" or "export" for its procedures.

Wraith Scheme's package system comprises some new functions with the following goals:

  • Allow any file of Scheme code to be loaded in such a way that all "define"s within it take place in a separate environment, and are not visible in either the top-level environment or in the environment from which the file-loading procedure was called, and

  • Allow the user to export, and optionally rename, any of the symbols defined in the file so loaded. Depending on which file-loading procedure is called, the export is either to

    • The top-level environment, or

    • The environment from which the file-loading procedure was called.

The way this mechanism works is based on Wraith Scheme's understanding of what an "environment" is. For a precise discussion, see the sections on "Environments" and on "Environment Lists" in the Wraith Scheme Dictionary. The idea is that the new functions operate by temporarily attaching a new environment at the front of either the top-level environment, or the environment from which the new function was called, then load the file into the environment thus modified. Any definitions in the file thus are visible only in the temporary attachment, and if the procedures in the file call one another, then the calls are resolved in the attachment so that the procedures work correctly with each other.

The user also provides a list of procedures from the file which are to be exported, and optionally renamed. The package procedure installs them in the appropriate environment.

There are two top-level procedures for load and export. I will also describe one lower-level procedure that is used as a primitive in the source code for the top-level ones.

  • Procedures for Packages:

    (c::load-with-environment-list <optional path to file> <environment list>)
    

      Loads the file into an extension on the environment list passed as the argument, and returns the extended environment list. The path to the file should be in the form of a string. If no path is provided, Wraith Scheme will use the Dialog Panel to obtain a pathname.

      This procedure is used as a primitive in constructing the higher-level procedures that follow.

    (e::load-and-export-to-top-level <path to file> <list of export descriptors>)
    

      Loads the file, and exports the functions that are described in the second argument, to the top-level environment.

      An export descriptor is either the name of a symbol defined in the file, whose value is to be exported to the top-level environment with the same name, or a list of two symbols, the first of which is the name of a symbol defined in the file, and the second of which is the name to be used when exporting it to the top-level environment.

    (e::load-and-export-to-calling-environment <path to file> <list of export-descriptors>)
    

      Loads the file, and exports the functions that are described in the second argument, to the environment from which it was called.

      An export descriptor is either the name of a symbol defined in the file, whose value is to be exported to the calling environment with the same name, or a list of two symbols, the first of which is the name of a symbol defined in the file, and the second of which is the name to be used when exporting it to the calling environment.

Now let's look at some examples. Suppose that file "SimplePackage.s" contains:

    (define (foo-internal) (display "This is foo-internal.\n"))
    

Suppose we evaluate the following expression at top level:

    (e::load-and-export-to-top-level "SimplePackage.s" '((foo-internal foo)))
    

We will then find that if we evaluate "(foo)" at top level, we see the message "This is foo-internal." Thus the procedure "foo-internal" from "SimplePackage.s" has indeed been exported, but renamed to "foo". If we try to evaluate "(foo-internal)" at top level, we will find that no binding of "foo-internal" exists at top-level.

Alternatively, suppose we had evaluated:

    (e::load-and-export-to-top-level "SimplePackage.s" '(foo-internal))
    

In that case, "SimplePackage.s" would have exported its binding of "foo-internal" to top level, but still named "foo-internal".

Now suppose that file "ComplexPackage.s" contains:

    (e::load-and-export-to-calling-environment "SimplePackage.s" '((foo-internal foo)))
    (define (bar-hidden)
      (foo)
      (display "This is bar-hidden\n"))
    

File "ComplexPackage.s" is set up to import "SimplePackage.s" and use one of the latter's definitions -- "foo-internal" renamed to "foo". We might then load "ComplexPackage.s" into top level by evaluating

    (e::load-and-export-to-top-level "ComplexPackage.s" '((bar-hidden bar)))
    

We would then find that if we evaluated "(bar)" at top level, we would get two messages, first "This is foo-internal", and then "This is bar-hidden", while the only new symbol defined at top level would be "bar".

You may nest packages arbitrarily in this manner.


Procedures and Forms:

This subsection describes some of the procedures and special forms that are present in Wraith Scheme, that are not part of the R5 standard. I have by no means described all such extras: If you explore the system with the various low-level inspection tools described herein, you will find many procedures I do not mention. I am not trying to keep secrets: Rather, I have described only those enhancements that seem stable enough to use in your own code. The ones not described may change in detail or disappear entirely in future releases of Wraith Scheme.

All the global symbols that I have used in constructing these enhancements start with either "e::" ('e' for enhancement) or with "c::" ('c' for compiler). To avoid naming conflicts with present and future enhancements, please do not create global symbols that start with either of these trios of characters.

    Technical Note: The double-colon in "e::" and "c::" may suggest some kind of "package" system, such as is widely used in other Lisps. Wraith Scheme has a package system -- described not far above -- but it is not used for these enhancements. The colon is an ordinary character. Symbols that contain a double colon are in no way different from symbols that do not. I use the double-colon in this way merely as a naming convention.

And please be wary about using any "c::" procedures in your own code. Some will cause a crash -- Wraith Scheme will cease to operate -- if misused.

  • Bit Operations:

    There are several procedures which allow direct manipulation of the individual bits of a 64-bit fixnum.

    (e::bit-and <integer> <integer>)
    

      Returns the bit-by-bit Boolean "and" of the arguments.

    (e::bit-not <integer>)
    

      Returns the bit-by-bit Boolean "not" of the arguments.

    (e::bit-or <integer> <integer>)
    

      Returns the bit-by-bit Boolean "or" of the arguments.

    (e::bit-xor <integer> <integer>)
    

      Returns the bit-by-bit Boolean exclusive "or" of the arguments.

    (e::bit-shift-left <integer> <integer>)
    

      Returns the first argument shifted left by the number of bits which is the second argument, inserting zeros at the right as necessary.

    (e::bit-shift-right-arithmetic <integer> <integer>)
    

      Returns the first argument shifted right by the number of bits which is the second argument, replicating the most-significant bit at the left as necessary.

    (e::bit-shift-right-logical <integer> <integer>)
    

      Returns the first argument shifted right by the number of bits which is the second argument, inserting zeros at the left as necessary.

  • Continued Fractions:

    See Long Ratnums and Continued Fractions.

  • Evaluation:

    In the past, there has been considerable controversy in the Scheme community concerning what "eval" ought to do, and whether it even ought to be part of the Scheme language at all. It took a long time for people to agree on the definition of "eval" used in R5 Scheme. Some of the procedures listed here predate that agreement and remain for backward compatibility. Some are low-level procedures used by various Wraith Scheme enhancements, and are mentioned here in case you wish to use them as primitives in your own work.

    (e::cons-with-continuation <object>)
    

      This procedure is left over from older releases of Wraith Scheme, that did not implement the "R5" Scheme standard. That standard has an "eval" procedure, which Wraith Scheme now supports. Yet "e::cons-with-continuation" is still there, in case anyone wants to use it.

      Many Lisps would call this procedure "eval". It causes its argument to be evaluated in the same environment as that in which "e::cons-with-continuation" itself was called.

        Technical Note: In case the name does not convince you that "e::cons-with-continuation" simply conses its argument onto the next-to-be-evaluated end of the current continuation, that is indeed what it does.

      Thus:

        (e::cons-with-continuation '(+ 2 2))    ;; ==> 4
        (define b 3)                            ;; ==> 3
        (define a 'b)                           ;; ==> a
        (e::cons-with-continuation a)           ;; ==> 3
        

      What is going on in that last example is that when you type

        (e::cons-with-continuation a)           ;; ==> 3
        

      the reader gets 'a, and the usual practice of evaluating the arguments to a procedure before calling it means that what is passed to "e::cons-with-continuation" is b. Procedure "e::cons-with-continuation" thus pushes b onto the next-to-be-evaluated end of the continuation, whereupon it is immediately evaluated to 3, which is what the top-level read-eval-print loop prints out.

      The main use of "eval" and similar features is a bit more complicated, however: One typically builds up the argument to be passed to eval a piece at a time, often in quite distinct and changing environments, then evals it all at once:

        (define to-be-evaled '())
        (set! to-be-evaled (cons 'operator to-be-evaled))
        (define x 42)
        (define y 88)
        (set-cdr! to-be-evaled (list 'x 'y))
        (define x 2)
        (define y 2)
        (define operator +)
        to-be-evaled                              ;; ==> (operator x y)
        (e::cons-with-continuation to-be-evaled)  ;; ==> 4
        

      In contrast, suppose we had slightly changed the line containing "set-cdr!" ...

        (define to-be-evaled '())
        (set! to-be-evaled (cons 'operator to-be-evaled))
        (define x 42)
        (define y 88)
        (set-cdr! to-be-evaled (list x y))        ;; NOTE: No quotes.
        (define x 2)
        (define y 2)
        (define operator +)
        to-be-evaled                              ;; ==> (operator 42 88)
        (e::cons-with-continuation to-be-evaled)  ;; ==> 130
        

    (e::push-list-into-continuation <proper-list>)
    

      This procedure pushes the items of a list, one at a time, cdr end first, into the continuation. It isn't the same as repeated application of "e::cons-with-continuation" (discussed immediately above), because none of the items pushed gets popped from the continuation until "e::cons-with-continuation" itself has completed execution.

      Since items of the list are pushed into the continuation cdr end first, they come out of the continuation in the order in which they appeared in the original list.

    (c::eval-in-environment <thunk> <environment-list>)
    

      Evaluates the given thunk in the given environment.

      This procedure is a generalization of the standard R5 Scheme "eval" procedure. The latter allows evaluation only in a particular set of environments; the present procedure allows evaluation in any Wraith Scheme environment list. For a precise discussion of what is meant by an environment list, see the section on "Environments" in the Wraith Scheme Dictionary.

    (c::symeval <symbol>)
    

      Returns the binding of the given symbol in the current environment. Informally, looks up the value of the symbol.

    (c::symeval-in-environment <symbol> <environment-list>)
    

      Returns the binding of the given symbol in the given environment. Informally, looks up the value of the symbol in the given environment.

      This procedure is a specialization of "c::eval-in-environment", described above: Its first argument must be a symbol, and its second a Wraith Scheme environment list. For a precise discussion of what is meant by an environment list, see the section on "Environments" in the Wraith Scheme Dictionary.

  • Files and Directories:

    (e::current-directory)
    

      Returns a string that is the full pathname of the "current directory", which is where Wraith Scheme operations look for files when you do not provide a complete pathname. The last character in the string returned will be a '/'.

    (e::error-port)
    

      Returns the port to which Wraith Scheme sends error messages. That is usually the console, even when Wraith Scheme is using a file for other output.

      This procedure allows you to write your own error handlers that send messages to the same port that Wraith Scheme uses.

    (e::file-exists? <string naming a file>)
    

      Returns #t or #f according to whether or not the pathname (full or partial) given by the string names a file that can be opened for reading by the Unix/C++ "fopen" function.

      The name of this procedure is slightly misleading, in that it is possible that the named file may exist but not have appropriate Unix permission settings for reading by the current user of Wraith Scheme. In that case, the procedure will return #f.

      There is no way to tell whether a returned value of #f means that the file does not exist, or that some directory along the path does not exist, or that there is some other problem: The procedure returns #f if for any reason it cannot find the indicated file, or cannot open it for reading.

      This procedure closes any file that it opens.

     (e::program-directory) 

      Returns the path to the directory containing the Wraith Scheme application itself; that is, a Unix path that starts with "/" and ends just before the name "Wraith Scheme.app". The last character in the string returned will be a '/'.

    (e::set-current-directory! <string naming directory>)
    (e::set-current-directory!)  ;; With no argument, uses a dialog.
    

      Changes the "current directory" to the directory whose pathname (full or partial) is given by the string. Appends a '/' to the pathname, if the pathname does not already end in a '/'. Returns the pathname, as a string. Reports an error when the string is not a valid pathname (full or partial) to a directory. The "current directory" is where Wraith Scheme operations look for files when you do not provide a complete pathname.

    (e::startup-directory)
    

      Returns a string that is the full pathname of the "startup directory"; that is, of the directory that was the "current directory" when you started Wraith Scheme. The last character in the string returned will be a '/'.

    (e::transcript-on-append)
    

      Works like R5 Scheme's "transcript-on" procedure, but opens the transcript file for "append": That is:

        If the given file does not exist, creates it and starts writing at the beginning.

        If the given file already exists, opens it and starts writing at the end, without overwriting any prior content.

      R5's regular "transcript-off" stops the use of any transcript file that is open.

  • Infinities and Nans:

    (e::nan? <object>)
    

      Returns #t if the <object> is an IEEE "nan" (which stands for "not a number"), otherwise returns #f.

    (e::inf? <object>)
    

      Returns #t if the <object> is an IEEE infinity (a special bit pattern used when floating-point operations have calculated numbers too large to be represented as ordinary IEEE floating-point numbers), otherwise returns #f.

  • Inspecting Scheme Objects:

    (e::inspect <object>)
    

      Displays some useful information about the <object> and returns the <object>. Just what is displayed depends on what the <object> is. The fact that "e::inspect" returns its argument means that you can generally wrap a call to "e::inspect" around any expression in a Scheme program, to see what is going on, without otherwise interfering with the operation of the program.

    (e::inspect-world <string naming a file>)
    (e::inspect-world)
    

      Assumes that the string is a path to a Wraith Scheme world file, and on that basis, attempts to open the world file and examine its contents, without actually making the file become the world in use by Wraith Scheme. Reports an error if there is insufficient memory to load the file, and may cause Wraith Scheme to fail and quit if the file is not actually a Wraith Scheme world.

      If no file name is given, puts up a standard dialog box for selecting a file to inspect.

  • Long Ratnums and Continued Fractions:

    (e::coerce-to-long-ratnum-if-possible <real>)
    

      If the <real> can be coerced to a long ratnum, returns it; otherwise returns #f

    (e::coercible-to-fixnum? <object>)
    

      Returns #t if the <object> is an integer in the range of 64-bit signed integers, and returns #f if not.

    (e::coercible-to-long-ratnum? <object>)
    

      Returns #t if the <object> is a number that can be expressed as a rational using 64-bit signed integers, and returns #f if not.

    (e::continued-fraction-list->real <list of integers>)
    

      Accepts a <list of integers> in the usual form for expressing a continued fraction, and returns the continued fraction as a long ratnum.

    (e::derationalize <real number>)
    

      If the argument is a rational number whose numerator and denominator are stored separately, divides the numerator by the denominator and returns the result; otherwise, returns the argument unchanged.

    (e::long-ratnum? <object>)
    

      Returns #t if the <object> is a rational number whose numerator and denominator are stored separately, and returns #f if not.

    (e::make-long-ratnum <integer> <integer>)
    

      Creates a rational number whose numerator and denominator are stored separately, in which the numerator is the first argument and the denominator is the second argument. The stored numerator will bear the sign of the result. The result will be exact if, and only if, both arguments are exact.

    (e::real->continued-fraction-list <real>)
    

      Accepts a <real> in the range where conversion to a long ratnum is possible, and return the list of integers that expresses it as a continued fraction in the usual form.

  • Macros -- An Alternate Implementation:

    In addition to the "hygienic" macro implementation described in the R5 report, Wraith Scheme provides a "lower-level" macro implementation, that is more like the macro implementations in older forms of Lisp.

      Technical Note: This older implementation of macros descends from Wraith Scheme's predecessor, Pixie Scheme, which I wrote before the R5 report was released. The older form is "non-hygienic", but is in some ways more versatile than what the R5 report describes, so I left it in as an enhancement.

      Besides, several key elements of Wraith Scheme are written as Scheme macros using the older macro implementation.

    (e::expand-macro <macro>)
    

      Expands a macro. Details follow in an example, a few paragraphs down.

    (e::macro? <object>)
    

      Returns #t if <object> is a macro, and returns #f if not.

    (e::macro <symbol> <expression>)
    

      Binds a macro to <symbol>. Other Lisp implementations might have used the name "defmacro" for this operation.

    (e::macro-body <expression>)
    

      Creates a macro from the <expression>, but does not bind it to a symbol. That is,

        (e::macro <symbol> <expression>)
        

      is equivalent to

        (define <symbol> (e::macro-body <expression>))
        

    Wraith Scheme implements this older form of macros in a rather conventional way. I will first describe this facility simply, glossing over some technical points, and will then fill in the details.

    To begin, "e::macro" resembles "define": The action of

      (e::macro <symbol> <expression>)
      

    is to evaluate the <expression>, to declare that whatever the evaluation returns is a macro, and finally to bind that macro to the <symbol>.

    The action of "e::macro-body" more nearly resembles the evaluation of a lambda expression: That is, the action of

      (e::macro-body <expression>)
      

    is merely to evaluate the <expression> and declare that whatever the evaluation returns is a macro.

    There is one catch: The final action of the <expression> must be to evaluate a lambda expression of one argument, so that the procedure thereby created is the value that will become the macro. Wraith Scheme reports an error if the value of the <expression> is not a procedure that was derived from a lambda expression of one argument.

    A macro is invoked, or "called", by evaluating a pair whose car evaluates to a macro. That is, if you have defined

      (e::macro my-macro ...)
      

    then you may invoke my-macro as

      (my-macro <stuff>)
      

    The action of such a call is (1) to pass the entire pair -- in this case "(my-macro <stuff>)" -- to the procedure associated with the macro; whereupon (2) that procedure does whatever it is supposed to do, to the pair; and (3) the result returned by that procedure is itself evaluated.

    Here is a simple example. Suppose you are tired of writing assignment statements in the form "(set! foo bar)", and would rather write them as "(assign bar to foo)" Define the macro:

      (e::macro assign (lambda (form) `(set! ,(cadddr form) ,(cadr form))))
      

    You can then write

      (define foo #f)      ;; ==> foo
      (define bar 3)       ;; ==> bar
      (assign bar to foo)  ;; ==> foo
      

    The last expression assigns bar, which evaluates to 3, to foo. Now

      foo                  ;; ==> 3
      

    Let's look closely at how that works. The macro call "(assign 3 to foo)", causes the entire expression, "(assign 3 to foo)" to be passed as the argument "form" to "(lambda (form) `(set! ,(cadddr form) ,(cadr form))))". That procedure returns a list whose first element is "set!", whose second element is the cadddr of the form (namely the symbol "foo"), and whose third element is the cadr of the form, namely the number 3. That list, "(set! foo 3)" is in turn evaluated, causing 3 to become the value of foo.

    You can see what is happening by evaluating

      (e::expand-macro '(assign 3 to foo)) ;; ==> (set! foo 3)
      

    This expression causes only the first two parts of the macro call "(assign 3 to foo)" to take place: It returns the unevaluated result from the procedure, namely the list "(set! foo 3)".

    If you want to see the lambda expression itself, instead of what happens when you apply it, use "e::inspect". When you enter

      (e::inspect assign)
      

    Wraith Scheme prints some information about where in Scheme memory the macro is located, then prints the lambda expression itself, though not as nicely pretty-printed as I have provided it here.

      (lambda (form)
        (quasiquote (set! (unquote (cadddr form))
                          (unquote (cadr form)))))
      

    Note in passing that "assign" is not a very well-behaved macro. For simplicity, it does not include various tests for syntax errors. A better version would have a lambda expression that checked that "form" was a list of exactly four elements, that the third element was the symbol "to", and that the fourth element was a symbol.

    The matter I have glossed over is what environments the various evaluations take place in. (Many people find the subject of environments confusing: You need not worry too much about this matter at first, though it may come back to haunt you if you become a serious Scheme enthusiast.)

    Briefly, the expansion of a macro -- that is, the evaluation of the procedure of one argument that is assigned to the macro name -- takes place in the lexical scope of the macro definition; but the second evaluation, of whatever expression the expansion returns, takes place in the lexical scope of the macro call.

    Here is an example that may make this issue clearer. Maybe ...

    First, let's define a global variable, named "my-variable", whose value is the symbol "global-value".

      (define my-variable 'global-value) ;; ==> my-variable
      my-variable                        ;; ==> global-value
      

    Now let's create a macro named "foo", whose body contains several instances of the symbol "my-variable".

      (e::macro foo
        (display "Defining foo -- my-variable is ")
        (display my-variable)
        (newline)
        (lambda ( the-argument-is-not-used )
          (display "Expanding foo -- my-variable was ")
          (display my-variable)
          (display " in the environment where foo was defined.")
          (newline)
          (display "In the present environment my-variable is ")
          'my-variable))
      

    As "foo" is being defined, the first two "display"s and the subsequent "newline" are evaluated, so that what appears in the Wraith Scheme window is

      Defining foo - my-variable is global-value
      foo
      

    The "display"s printed the value of "my-variable" in the environment where foo was defined; that value was still "global-value". The lambda expression was then converted into a procedure, which became the value of "foo", which was returned and displayed at top level. The procedure uses "my-variable" in the fifth-from-last line of its definition, in "display". The value of "my-variable" so referenced is again from the environment in which "foo" was defined. So when "foo" is called, it will print (reformatted here to make the lines shorter):

      Expanding foo -- my-variable was global-value
        in the environment where foo was defined.
      

    before the lambda expression returns any value.

    The value returned by the lambda expression is a quoted symbol, "'my-variable", and that leading quote is a key to understanding what happens next. This expression will be evaluated in the environment in which the call to "foo" took place. In that environment, "my-variable" will not necessarily have the same value that it did in the environment where "foo" was defined. Thus after the return value has itself been evaluated, it will become

      "In the present environment my-variable is <?>"
      

    We don't now know for sure what the "<?>" will be.

    To continue the example, let's create a local environment in which "my-variable" has as value the symbol "local-value", and call "foo" in that environment. We might evaluate

      (let ()
        (begin (define my-variable 'local-value) (foo)))
      

    What gets printed is, after reformatting it to make the lines shorter:

      Expanding foo -- my-variable was global-value
        in the environment where foo was defined.
      In the present environment my-variable is local-value
      

  • Miscellaneous Predicates:

    (e::all? <list>)
    

      Returns #t if no object in the <list> is #f, and returns #f otherwise. Returns #t if the <list> is the empty list. This enhancement is a procedure, hence it will evaluate every item in its list of arguments.

    (e::any? <list>)
    

      Returns #t if at least one object in the <list> is not #f, and returns #f otherwise. Returns #f if the <list> is the empty list. This enhancement is a procedure, hence it will evaluate every item in its list of arguments.

    (e::atom? <object>)
    

      Returns #t if <object> is an atom, and returns #f otherwise.

        Technical Note: An atom is a Scheme object that does not contain any pointers or other references to any other Scheme objects. Just what objects are atoms is implementation-dependent.

    (e::closed-port? <object>)
    

      Returns #t if <object> is an input port or an output port which has been closed, and returns #f otherwise.

    (e::fixnum? <object>)
    

      Returns #t if the <object> is a number stored as a 64-bit integer, otherwise returns #f. All numbers so stored are integers.

    (e::float? <object>)
    

      Returns #t if the <object> is stored in an IEEE floating-point format, otherwise returns #f.

      These formats can represent integers, rationals which are not integers, IEEE nans and IEEE infinities. Wraith Scheme finds occasion to use IEEE formats for all of these entities.

    (e::forced? <object>)
    

      Returns #t if <object> is a promise which has been forced, and returns #f otherwise.

    (e::long-complex? <object>)
    

      Returns #t if the <object> is a number stored in an implementation-dependent format that separately stores the real and imaginary part of a complex number, otherwise returns #f.

    (e::promise? <object>)
    

      Returns #t if <object> is a promise, and returns #f otherwise.

  • Miscellaneous Procedures:

    (e::deep-copy <object>)
    

      Constructs a copy of its argument which is "equal?" to the argument, but in which copied vectors, lists and strings in corresponding positions in the structure are not themselves "eq?" to one another.

    (e::error <string>)
    

      Prints a string to the default error port, and resets Wraith Scheme to the top-level loop.

    (e::warning <string>)
    

      Prints a string to the default error port, and then continues; does not reset Wraith Scheme to the top-level loop.

    (e::gensym)
    

      Returns a symbol guaranteed to be different from any symbol already encountered by Wraith Scheme. Symbols created by "e::gensym" print as strings of the form "gXXXXX...", where "XXXXX..." are the digits of a positive decimal integer that is greater than or equal to 23000.

        Technical Note: The choice of "23000" is in memory of Ratfor. "Ratfor" is not the name of one of my cats; at least, not yet.

    (c::load-from-string <string>)
    

      Causes the literal text of the string argument to be read at top level by the Wraith Scheme read-eval-print loop, which will presumably evaluate it and print the result.

        Technical Note: This procedure acts very simply, by "pushing back" the text of its argument into the queue of characters from which Wraith Scheme obtains input.

    (e::make-integer-range <integer> <integer> <nonzero integer>)
    

      Returns a list of integers that starts at the first argument and advances toward the second argument, but not to or past it, in steps of the third argument. Thus for example:

        (e::make-integer-range 0 10 1)
        

      returns (0 1 2 3 4 5 6 7 8 9).

    (e::original-cwcc <object>)
    

      Wraith Scheme implements "dynamic-wind" generally along the lines of the implementation demonstrated by Jonathan Rees (1992) in "The Scheme of Things: The June Meeting", published in Lisp Pointers V(4), October-December 1992. That implementation involves modifying "call-with-current-continuation". The unmodified version of "call-with-current-continuation" is available as "e::original-cwcc".

    (e::read-string-with-prompt <string>)
    

      Uses the Wraith Scheme Dialog Panel to display the string that is its argument, and to accept a different string, which is returned. The string you type into the dialog box does not require enclosing double-quotes.

    (e::reduce <binary-operation> <list> [<start-value>])
    

      Applies the given operation to the first two elements of the list, then applies the given operation to the result and the next element of the list, and so on. If the optional start value is given, starts out by applying the given operation to the start value and the first element of the list. For example:

        (e::reduce + (list 2 3 4 5) 42)
        

      calculates ((((42 + 2) + 3) + 4) + 5), which is 56.

    (e::select <predicate> <list> [<key>])
    

      With no <key>, makes up a new list comprising all elements of the given list for which the <predicate> is true. If the optional <key> is present, the <predicate> is applied to whatever results from applying the <key> to the list elements. Thus:

        (e::select positive? '(1 -1 0 2 -2 3 -3))
          ;; ==> (1 2 3)
        

      but

        (e::select positive? '((a 1) (b -1) (c 0) (d 2) (e -2) (f 3) (g -3) ) cadr)
          ;; ==> ((a 1) (d 2) (f 3))
        

      Thus for example, the list elements may be some sort of structure, and the <key> gives you the chance to pull out a particular part of the structure for testing by the <predicate>: In the case just shown, the structure is a list of two items, and the particular part is the second item.

      You could always do the same thing by using a specialized lambda expression for the <predicate>, but sometimes using the <key> makes things clearer.

    (e::system <string>)
    

      Passes the given string to the Unix/C++ "system" function, for execution, and returns whatever "system" did; that is, an integer having to do with whether the execution was successful. (See the Unix documentation for "system".) For example,

        (e::system "cal > ~/calendar.out")
        

      puts a calendar for the present month in a file called "calendar.out" in your home folder.

      This procedure is very powerful; it is a doorway into the capability of the the entire Unix system. Unfortunately, there is no easy way to obtain the details of what happened, other than recording them to a file (as in the example just given) for subsequent examination.

        Technical Note: Actually, that isn't quite true. Any terminal output from the Unix/C++ "system" function ends up in the Macintosh console log file. (See the "Console" application, in the "Utilities" folder, in the "Applications" folder.) If all you need to do is look at the results, that might be enough; but if you want Wraith Scheme to process the results and do something with them, you will have to redirect them to a file, as in the example just given, and have Wraith Scheme read them in from there.

    (e::time)
    

      Accesses the Unix/C++ "time" function, which returns an integer that is the number of seconds since the start of calendar year 1970, common era.

    (e::usleep <integer>)
    

      Calls the Unix/C++ "usleep" function with the given integer as argument. Nominally, this function makes the Wraith Scheme interpreter sleep -- ignore input and do nothing -- for a number of microseconds which is equal to the given integer. Because of delays in starting up the procedure, the granularity of the period of sleep is much coarser than one microsecond; the calling interface merely provides consistency with the Unix/C++ function.

      Only the part of Wraith Scheme that reads, evaluates, and prints Scheme expressions is put to sleep by this command. Wraith Scheme will respond to many user actions while sleeping; for example, changes in window size. Actions that would cause the processing of Scheme expressions will not take place until Wraith Scheme has waked up, and may block other user actions until wake-up has taken place.

      In principle, calling "e::usleep" with a sufficiently large argument could cause Wraith Scheme to sleep for an objectionable length of time. Users of this command might well learn how to force Wraith Scheme to quit by asking the Macintosh Finder to assist: Hold the "ctrl" key down while clicking on the Wraith Scheme icon in the Dock.

    (e::version)
    

      Returns a string that shows the date and time of creation of the version of the Wraith Scheme program that is running.

  • Multiple-Values Objects and Operations:

    Wraith Scheme implements the procedures "values" and "call-with-values", as described in the "R5" report. The implementation features an additional kind of Scheme object, which I have unimaginatively labeled a "multiple values return". The intended use of these objects is to pass data between "values" and "call-with-values", but if you should happen to call "values" in some context other than providing input to "call-with-values", you might encounter one. Thus, for example, at top level:

      (values 2 3 4)  ;;  ==>
      
      #<Multiple Values Return>
      List of values: (2 3 4)
      

    Furthermore:

    (e::multiple-values? <object>)
    

      Returns #t if the <object> is a multiple-values return, otherwise returns #f.

  • Numeric Formatting:

    Straight R5 Scheme provides no convenient way to format numbers in varying ways without writing a lot of code on your own. Wraith Scheme has a few enhancements to do some of the common formatting tasks, and perhaps to make it easier to create your own code to do more such tasks.

    The interface is built on a single low-level procedure, which uses the numeric formatting capabilities of the underlying C++ implementation in which Wraith Scheme is written. (I do not propose to tell you how to do numeric formatting in C in this document, since there are plenty of widely available sources that do a better job of that than I could. For example, try typing "man printf" in a Unix shell, such as the Macintosh "Terminal" application.)

    The low-level procedure is:

    (c::number->string-with-c-format <number> <string>)
    

      This procedure accepts a number as first argument, coerces it to a C++ "double", and converts it to a number using the second argument as a C++ "format" string.

        Technical Note: The underlying C++ call ends up as
          snprintf( scratch, 256,
                    <your number coerced to double>,
                    <your format string>)
          

          in which "scratch" is a pointer to a buffer containing 256 characters. Following this C++ call, the content of "scratch" is again copied into a newly-allocated string in Wraith Scheme's main memory, so there is no need to worry about saving "scratch" or overwriting it.

        Note that "c::number->string-with-c-format" does no checking whatsoever of whether its "string" argument is appropriate for use as a C++ "format" string. Thus there is a noteworthy probability that ill-considered use of this procedure may cause Wraith Scheme to crash. This procedure is perhaps best used as a primitive for procedures which are themselves well tested and debugged.

        Thus for example:

          (c::number->string-with-c-format 1 "%.3f")
          ;;  ==> "1.000"
          
          (c::number->string-with-c-format 29.95
            "For the low, low price of only $%.2f!!")
          ;;  ==> "For the low, low price of only $29.95!!"
          

      There are two additional procedures which operate at slightly higher level:

      (e::number->string-with-n-decimals <number> <number>)
      

        This procedure returns a string containing the first argument printed in decimal format with n digits to the right of the decimal point, where n is the second argument. The value of n must be in the range [0 .. 10].

        For example:

          (e::number->string-with-n-decimals 1/3 3)
          ;;  ==> "0.333"
          
          (e::number->string-with-n-decimals (- (/ 1000 3)) 6)
          ;;  ==> "-333.333333"
          

      (e::money->string <number>)
      

        Prints the number with two digits to the right of the decimal point, as in:

          (e::money->string 29.95)
          ;;  ==> "29.95"
          

    • Permanence:

      If only one value will ever be bound or assigned to a particular symbol, you can speed up the operation of both interpreted and compiled code by making that symbol "permanent". You can reverse the process later, if necessary; but if you do, be sure to recompile any compiled expressions that use the symbol: Otherwise, strange things may happen. It is possible -- though perhaps not useful -- to make permanent a symbol that already has different values bound to it in different scopes.

      Wraith Scheme will report an error if you attempt to bind or to assign a value to a permanent symbol.

      Most of the symbols that denote the built-in primitive operations of Wraith Scheme are permanent. These symbols include "+", "car", "if", "call-with-current-continuation" and so on.

      (e::set-permanent! <symbol>)
      

        Makes the value assigned to <symbol> be permanent; that is, declares to Wraith Scheme that the value presently assigned to <symbol> will never change. Does not change that value.

      (e::clear-permanent! <symbol>)
      

        Makes the value assigned to <symbol> be non-permanent; that is, declares to Wraith Scheme that the value presently assigned to <symbol> may change. Does not change that value.

      (e::permanent? <symbol>)
      

        Returns #t if the <symbol> is permanent, otherwise returns #f.

          Technical Note: I added the "permanent" feature because in another Lisp system, I once inadvertently and unknowingly redefined "T" -- its "true Boolean". That system kept a master value of T somewhere, to copy into code when necessary. Somehow, I changed it. Code that already had the "right" value of T continued to work correctly, but new stuff did not. The effect was that I had a weird bug, and whenever I changed a procedure, in the act of debugging, the bug spread. Unreal ...

          The folks at MIT's project MAC, who wrote the original "MACLisp" (a 1970's-vintage Lisp that ran on mainframe computers, not -- as you might suppose from the name alone -- a Lisp for the Macintosh) issued special error messages if you tried to alter the values of T and nil in that system. They were:

            VERITAS AETERNA -- do not setq T!
            NIHIL EX NIHIL -- do not setq nil!
            

          They had the right idea. (Welcome to the twilight zone -- a CalTech graduate -- me -- just said something complimentary about MIT.)

          Perhaps you can figure out how I know about those error messages ...

          Elsewhere herein I tell how, in the early stages of development of Pixie Scheme -- Wraith Scheme's predecessor -- I accidentally managed to create nearly nine billion separate and operationally inequivalent kinds of truth and falsehood.

          Non-Technical Note: According to artificial-intelligence historian Pamela McCorduck, what "MAC" stood for was either "Man And Computers" or "Machine-Aided Cognition", and the ambiguity was deliberate. See the footnote on p. 287 of Machines Who Think, which is listed in the Other References section herein.

          I have occasionally wondered whether Jeff Raskin deliberately spelled "Macintosh" the way he did in order to include a subtle reference to project MAC. Or maybe he just thought somebody was all wet and needed a raincoat.

    • Print Length and Depth:

      Eight procedures deal with parameters that control how much of a complicated list or vector gets printed, and thereby incidentally prevent Wraith Scheme from entering an infinite loop when asked to print a circular data structure. There are four such parameters, but their values are not directly available: You must use the functions to manipulate them. One parameter limits the number of elements of a list that will be printed. If this parameter is 3, then '(1 2 3 4 5) will print as

        (1 2 3 ...)
        

      The second parameter similarly limits the number of elements of a vector that will be printed.

      The third parameter limits the depth of nested lists that will be printed. If that parameter is 3, then '(((((foo))))) will be printed as

        (((...)))
        

      The fourth parameter similarly limits the depth of nested vectors that will be printed.

      (e::list-print-depth)
      

        Returns the current maximum depth to which nested lists will be printed.

      (e::list-print-length)
      

        Returns the current maximum number of elements of a list that will be printed.

      (e::vector-print-depth)
      

        Returns the current maximum depth to which nested vectors will be printed.

      (e::vector-print-length)
      

        Returns the current maximum number of elements of a vector that will be printed.

      (e::set-list-print-depth! <positive integer>)
      

        Sets the current maximum depth to which nested lists will be printed. The <positive integer> must exceed two.

      (e::set-list-print-length! <positive integer>)
      

        Sets the current maximum number of list elements that will be printed. The <positive integer> must exceed two.

      (e::set-vector-print-depth!  <positive integer>)
      

        Sets the current maximum depth to which nested vectors will be printed. The <positive integer> must exceed two.

      (e::set-vector-print-length! <positive integer>)
      

        Sets the current maximum number of vector elements that will be printed. The <positive integer> must exceed two.

    • Random Numbers:

      Two procedures provide access to a quite good Unix random number implementation:

      (e::random)
      

        Returns a random integer generated by the Unix/C++ function "random" (which should not be confused with the earlier random-number implementation, "rand").

      (e::srandom <integer>)
      

        Seeds the Unix/C++ "random" implementation with the given integer.

      Wraith Scheme itself seeds the "random" implementation with a different number every time it starts up. If you wish to generate a repeatable string of random numbers -- perhaps for debugging a program that uses "e::random" -- re-seed the implementation, via "e::srandom", with the same integer, before each sequence of calls to "e::random".

      See the Unix documentation for details of "random" and "srandom".

    • Speech Synthesis:

      One procedure provides simple access to the Macintosh's built-in speech-synthesis capability.

      (e::string->speech)  ;; With no argument, uses a dialog.
      (e::string->speech <string>)
      

        Speaks the string, using the voice and voice settings given in your Macintosh's System Preferences.

    • Sorting and Merging:

      Five procedures deal with sorting and merging. These provide similar functionality to that in Scheme SRFI 95, which is on line at http://srfi.schemers.org/srfi-95/srfi-95.html as I write these words. (SRFI means "Scheme Request For Implementation", and refers to a large body of code specifications which the Scheme community has decided would be useful if generally available.) I cannot implement that SRFI precisely because it depends on another SRFI which I have not implemented, but I chose to provide similar functionality.

      The merge and sort procedures are stable when used with comparison predicates that return #f when applied to identical arguments.

      See the source code file "SortMerge.s", which is provided as part of the open-source distribution of Wraith Scheme and Pixie Scheme III, for details of the origin and copyright of the source code for these functions. In essence, much of that code derives from the work of Aubrey Jaffer, and is in public domain.

      (e::merge <list> <list> <comparison procedure> [<key>])
      

        Merges two sorted lists into sorted order, in order from "least" to "greatest" in the sense described below. The result is a new list. Neither of the original lists is changed by this procedure.

        Without the optional <key>, merges in sorted order, two lists which have already been sorted using the <comparison procedure>. That procedure might descriptively be named "less?", and is presumed to accept two arguments and to return a boolean value indicating whether the first argument is "less" than the second. For example:

          (e::merge (list 1 3 5) (list 2 4) <)  ;;==> (1 2 3 4 5)
          

        If the optional <key> is present, then the procedure for deciding which of two elements is less, during both the initial sorts of the separate lists and the merge itself, is that the <key> is first applied to the elements being compared, and then the <comparison procedure> is applied to the results thereby obtained. A common use of the <key> is if the elements being compared are some kind of structure, such as a list or vector, and the "less?" procedure is intended to apply to only one element of the structure. For example, if the lists are themselves composed of two-element lists, and the desired comparison is based on the second elements of those lists, then the <key> "cadr" could be used to extract the second elements for comparison. For example:

          (e::merge (list '(a 1) '(b 3) '(c 5)) (list '(d 2) '(e 4)) < cadr)
            ;;==> ((a 1) (d 2) (b 3) (e 4) (c 5))
          

        You could always do the same thing by using a specialized lambda expression for the <predicate>, but sometimes using the <key> makes things clearer.

      (e::merge! <list> <list> <comparison procedure> [<key>])
      

        Like "e::merge" (immediately above), except that it destroys the structure of both of the lists that are its arguments, and that the returned list will be composed of some of their components.

      (e::sort <list or vector> <comparison procedure> [<key>])
      

        Sorts either a list or a vector -- your choice -- into a new instance of the same kind of entity. The original list or vector is not changed by this procedure. The sorted result will be in order from "least" to "greatest" in the sense described below.

        Without the optional <key>, uses the <comparison procedure> to decide how to sort. That procedure might descriptively be named "less?", and is presumed to accept two arguments and to return a boolean value indicating whether the first argument is "less" than the second. For example:

          (e::sort (vector 2 3 1) <)  ;;==> #(1 2 3)
          

        If the optional <key> is present, then the procedure for deciding which of two elements is less is that the <key> is first applied to the elements being compared, and then the <comparison procedure> is applied to the results thereby obtained. A common use of the <key> is if the elements being compared are some kind of structure, such as a list or vector, and the "less?" procedure is intended to apply to only one element of the structure. For example, if the lists are themselves composed of two-element lists, and the desired comparison is based on the second elements of those lists, then the <key> "cadr" could be used to extract the second elements for comparison. For example:

          (e::sort (list '(a 2) '(b 3) '(c 1)) < cadr)
            ;;==> ((c 1) (a 2) (b 3))
          

        You could always do the same thing by using a specialized lambda expression for the <predicate>, but sometimes using the <key> makes things clearer.

      (e::sort! <vector> <comparison procedure> [<key>])
      

        Like "e::sort" (immediately above), except that it only sorts vectors, and it sorts the given vector by permuting the elements within it, so that the returned vector is the same as (in the sense of "eq?") the original vector, but has had its elements sorted.

      (e::sorted? <list or vector> <comparison procedure> [<key>])
      

        Determines whether the given list or vector is sorted, in order from "least" to "greatest" in the sense described below.

        Without the optional <key>, uses the <comparison procedure> as the basis for deciding whether one element is less than another. That procedure might descriptively be named "less?", and is presumed to accept two arguments and to return a boolean value indicating whether the first argument is "less" than the second. For example:

          (e::sorted? (vector 1 2 3) <)  ;;==> #t
          

        If the optional <key> is present, then the procedure for deciding which of two elements is less is that the <key> is first applied to the elements being compared, and then the <comparison procedure> is applied to the results thereby obtained. A common use of the <key> is if the elements being compared are some kind of structure, such as a list or vector, and the "less?" procedure is intended to apply to only one element of the structure. For example, if the lists are themselves composed of two-element lists, and the desired comparison is based on the second elements of those lists, then the <key> "cadr" could be used to extract the second elements for comparison. For example:

          (e::sorted? (list (c 1) (a 2) (b 3)))  ;;==> #t
          

        You could always do the same thing by using a specialized lambda expression for the <predicate>, but sometimes using the <key> makes things clearer.

    • State Flags:

      Several procedures allow Scheme programs to examine and modify the internal flags that control whether defines are compiled automatically, and whether the display routines for numbers use the full precision available.

      (e::set-compiler-on!)
      (e::clear-compiler-on!)
      (e::compiler-on?)
      

        The preceding procedures respectively set, clear, and return the value of the internal flag that controls whether Wraith Scheme compiles define automatically. The procedures that change the flag take effect at once, but the corresponding displays in the Wraith Scheme Instrument Panel and in the Interpreter Menu may not update immediately. Be patient: They will catch up when Wraith Scheme isn't busy.

      (e::set-show-full-precision!)
      (e::clear-show-full-precision!)
      (e::show-full-precision?)
      

        The preceding procedures respectively set, clear, and return the value of the internal flag that controls whether Wraith Scheme display routines use the full precision available. The procedures that change the flag take effect at once, but the corresponding displays in the Wraith Scheme Instrument Panel and in the Interpreter Menu may not update immediately. Be patient: They will catch up when Wraith Scheme isn't busy.

    • Storage Management:

      (e::active-room)
      

        Returns an integer, which is the number of bytes of storage space available in the current Wraith Scheme active generation that are unused. When this number becomes too small, a generational garbage collection must take place -- and will take place automatically -- as an attempt to free up some space.

      (e::active-store-size)
      

        Returns an integer, which is the total number of bytes in the current Wraith Scheme active generation. This number is necessarily the sum of the number of bytes used there, and the number that remain available.

      (e::aging-room)
      

        Returns an integer, which is the number of bytes of storage space available in Wraith Scheme aging generation, that are unused. When generational garbage collection is in use, this number will typically be either zero or very close to it, since the current aging generation is merely the previous active generation after it has become so large that generational garbage collection was necessary.

      (e::full-gc)
      

        Performs garbage collection. Wraith Scheme will also automatically invoke this routine when necessary.

          Technical Note: The garbage-collection algorithm used by Wraith Scheme compacts lists quite efficiently, using "cdr coding" wherever possible.

        The speed of garbage collection will vary from fast -- when the Wraith Scheme main memories are contained entirely within physical memory chips -- to very slow -- when the Wraith Scheme main memories are in part stored as virtual memory, on disk. It takes a long time to "swap"; that is, to move data back and forth between disk and physical memory. On a 2006 model Macbook, with a 2 GHz Intel Core Duo processor and 1 GByte of memory, with no other user processes but Wraith Scheme running, with a Wraith Scheme main memory size of nearly 1 GByte, with a substantial portion of main memory containing non-garbage, I have seen garbage collection take as long as twenty minutes.

        Newer Macintosh computers are more powerful, but Wraith Scheme can use that power, so garbage collection still can take lots of time. On a Mac Mini with an 8-core Apple M1 processor and 8 GB of memory, with no other user processes but Wraith Scheme running, with a Wraith Scheme main memory size of 64 GByte, I have seen garbage collection take as long as half an hour.

          Technical Note: Most of the swapping takes place early in the garbage-collection process, and I have no way to monitor the progress of swapping, so there isn't much point in using a progress indicator to show what is going on; it would just sit at a fixed position for a long time, then rapidly zoom through the rest of its range.

      (e::ninety-percent-used?)
      

        Returns a boolean, which is true if the amount of storage space in Wraith Scheme main memory, that is in use by Wraith Scheme, exceeds ninety percent of the total amount of storage space there available.

      (e::room)
      

        Returns an integer, which is the number of bytes of storage space available in Wraith Scheme main memory, that are unused. When this number becomes too small, a full garbage collection must take place -- and will take place automatically -- as an attempt to free up some space.

      (e::show-room)
      

        Shows some information about how much storage space has been used, and about how much remains available. Returns #t.

      (e::store-size)
      

        Returns an integer, which is the total number of bytes in either one of the two storage spaces used by Wraith Scheme. This number is necessarily the sum of the number of bytes used and the number that remain available. It is also one of the numbers printed out at the top of the Wraith Scheme window when the program begins.

    • System Information:

      This section deals with procedures that allow examination or modification of some of the internal data structures used by Wraith Scheme. Abuse of some of these functions will cause fatal errors, crashes and other disasters. Detailed description of these data structures is beyond the scope of this help file; however, experienced Lisp programmers will know what I am talking about.

      (e::bound-instance? <symbol>)
      

        Returns #t if the <symbol> has a value in the environment in which "e::bound-instance?" is called, otherwise returns #f. Many Lisp systems would call this function "boundp".

      (e::get-tag <object>)
      

        Returns a positive integer that indicates the type of <object> and conveys information about how <object> is cdr-coded.

      (e::set-tag! <object> <integer>)
      

        Changes Wraith Scheme's understanding of what kind of thing <object> is. Returns <object>, with its new tag. Warning: Misuse of this function will cause Wraith Scheme to crash. (Indeed, almost any use of this function will cause Wraith Scheme to crash.)

      (e::show-active-memory)
      

        Shows an extremely lengthy display of all objects in that part of the active generation that is presently in use by Wraith Scheme. Returns #t.

        Fair warning: This procedure can generate enormous amounts of output.

      (e::show-aging-memory)
      

        Shows an extremely lengthy display of all objects in that part of the aging generation that is presently in use by Wraith Scheme. Returns #t.

        Fair warning: This procedure can generate enormous amounts of output.

      (e::show-dynamic-environment-list)
      

        Shows all symbols, and their values (if any), that are visible in the lexical scope from which this procedure was called, except for those in the top-level environment. Returns #t. The display will consist of a sequence of several similar displays. The first one will show all symbols in the innermost lexical scope visible to the caller, then the next innermost lexical scope, and so on out to, but not including, the top-level, "global" environment.

      (e::show-environment)
      

        Shows all symbols, and their values (if any), that are visible in the innermost lexical scope from which this procedure was called. Returns #t.

      (e::show-environment-list)
      

        Shows all symbols and their values (if any) that are visible in the lexical scope from which this procedure was called. Returns #t. The display will consist of a sequence of several similar displays. The first one will show all symbols in the innermost lexical scope visible to the caller, then the next innermost lexical scope, and so on to the top-level, or "global" environment. This last display will be extremely lengthy, for it will include all the Wraith Scheme primitives, like "+", "car" and "cons".

      (e::show-memory)
      

        Shows an extremely lengthy display of all objects in the part of main memory that is presently in use by Wraith Scheme. Returns #t.

        Fair warning: If you call this procedure when Wraith Scheme is using a large main memory -- say a gigabyte -- and if that memory is full, or nearly so, the procedure may well attempt to generate hundreds of millions of lines of output.

      (e::show-stack)
      

        Shows the contents of the internal stack used by Wraith Scheme. Returns #t. This stack is different from the "machine stack", and has nothing to do with any processor "stack" register. Also see the procedure "e::write-stack".

      (e::stack-depth)
      

        Returns an integer, which is the number of objects on Wraith Scheme's internal stack.

      (e::write-continuation)
      

        Writes out the current continuation as a list. Returns #t. I use "continuation" here in the sense of the 'C' register of an SECD machine.

      (e::write-stack)
      

        Writes out the contents of Wraith Scheme's internal stack as a list, with the most recent object pushed as the car. Returns #t. Also see the procedure "e::show-stack".

    • Top-Level Control:

      Several procedures allow top-level control of the operation of the Wraith Scheme application:

      (e::exit)
      

        Causes Wraith Scheme to terminate. It's how you get back to (e. g.) the Finder. The "e::exit" procedure can also be invoked via the "Quit" command or the "Quit" menu item, or by clicking on the red "close window" widget -- the little red dot -- at top left in the Wraith Scheme Window.

        In Wraith Scheme parallel processing, the MomCat is essential: Thus when you terminate the MomCat by any of these means, Wraith Scheme will also terminate all other Wraith Scheme processes -- all the kittens -- that are running. That is, in the MomCat, "e::exit", the "Quit" command, the "Quit" menu item, and the little red dot all act as global "kill" commands for all Wraith Scheme processes that you are running.

        When you use the "Quit" command or menu item, or click on the "close window" widget, Wraith Scheme will ask you to confirm that you really want to exit. (Otherwise a simple error -- perhaps accidentally picking the wrong menu item, could lose of a lot of work.) But when you evaluate "(e::exit)" in the Wraith Scheme window, or in a program, there will be no dialog, the program will just quit.

      (e::reset)
      

        Stops whatever Wraith Scheme is doing, and returns control to you. You can also invoke the "e::reset" procedure from the "Reset to Top-Level Loop" menu item or the corresponding keyboard command, "option-shift-command-delete". If Wraith Scheme is doing the "wrong thing" -- perhaps a program has entered an infinite loop -- the keyboard and menu commands will force execution to halt. Within a Scheme program, "e::reset" is useful for error handling, as a way to stop execution without returning some kind of error flag back through many procedure calls.

      (e::set-input-port!)  ;; With no argument, uses a dialog.
      (e::set-input-port! <string with a path to a file>)
      

        "e::set-input-port!" is a procedure that only a developer could love. It makes a permanent change in where the system gets its top-level input. Its intended use is in testing error-handling, in the following way: When "e::reset" is evaluated, it normally returns control to the Wraith Scheme window, so that the next input to Wraith Scheme after an "e::reset" evaluation is whatever is next typed into the window. But if "e::set-input-port!" has been called, then the next input after the reset will be taken from the port passed to "e::set-input-port!". Thus to perform many tests of a program that calls "e::reset" for error handling, you might create a text file containing those tests, enter Wraith Scheme, open a transcript, and call "e::set-input-port!" with the name of that text file. The tests will start to run, and when "e::reset" is called, Wraith Scheme will perform the next test in the file, instead of returning control to the Wraith Scheme window.

        Note that you cannot use "load" instead of "e::set-input-port!" for the same effect: If "e::reset" were called by any of the forms being loaded, then the "load" procedure itself would terminate immediately, and control would return to the Wraith Scheme window.

        This procedure will only work correctly when called from top-level; in particular, it will not work when called by code that is being executed via the "load" procedure.

      (c::disable-window-output)
      (c::enable-window-output)
      (c::window-output-enabled?)
      

        These procedures allow you to control whether Wraith Scheme writes output to the Wraith Scheme window or not. The first two respectively disable and enable that output, the third returns a boolean indicating whether output is enabled or not.

        Output to files, including transcript files, is not affected by these procedures. Furthermore, anything you type into the Wraith Scheme Input Panel will be echoed in the Wraith Scheme window, even if window output is disabled.

        These procedures use the same mechanism to control window output as does the "Disable Window Output" menu item, in the Interpreter Menu, but the procedures do not cause display of the panel that drops down from the top of the Wraith Scheme window to remind you that output is disabled.

      One procedure causes Wraith Scheme to crash:

      (c::down-in-flames!!!)
      

        DANGER!! This procedure causes Wraith Scheme to fail. (If not, it's a bug.)

        I created this procedure so that I could easily test Wraith Scheme's mechanism for reporting fatal errors.

    • Unix Sockets:

      Several procedures allow a Wraith Scheme process to act as a server for Unix sockets.

      These procedures have been in Wraith Scheme for some time, but were undocumented earlier: I am by no means a socket wizard, and the procedures are a bit primitive, so it is a bit embarrassing turning them loose. Still, they might be useful for some purposes. For a better socket interface, you might write separate C++ programs to deal with sockets, and use them by way of the Wraith Scheme foreign-function interface.

        Technical Note: Let's be very clear: I am talking about the kind of sockets that allow separate processes on the same computer to communicate. These are not the kind of sockets that allow processes on different computers to communicate by way of the Internet.

      Detailed discussion of the use of Unix sockets is beyond the scope of the Wraith Scheme help file.

      (c::socket-create-and-listen <string-naming-socket> <backlog>)
      

        Attempts to create and listen to a Unix server socket with the given name and backlog, and returns the socket number if successful.

          Technical Note: Executes the Unix/C++ functions "socket", "bind" and "listen".

      (c::socket-accept <socket number>)
      

        Wait for a connection to be made on the given socket. When (and if ever) successful, return the socket number of the accepted socket.

          Technical Note: Executes the Unix/C++ function "accept", which is prepared to wait forever for a connection to be made.

      (c::socket-write <socket number> <string>)
      

        Write some text to a socket.

          Technical Note: A straight Unix/C++ "write".

      (c::open-input-socket <socket number>)
      

        Opens the given socket number for input; that is, to be read from. Returns a Wraith Scheme input port for doing the reading.

      (c::socket-fgets <input port connected to a socket>)
      

        Reads up to 128 bytes from the given input port and returns what was read as a Scheme string.

          Technical Note: The choice of 128 bytes was a bit arbitrary. I did say that the Unix-socket procedures were a little primitive ...


    Sensory Input Devices:

    The Wraith Scheme Sensory Devices Drawer opens on the right side of the Wraith Scheme window. You can open it with an item in the Wraith Scheme Window Menu. That drawer contains a some input devices that you can mouse on in order to communicate with Wraith Scheme: It has five pushbuttons, four sliders, and eight sense switches. Here is an image showing what they look like.

    The Wraith Scheme Sensory Devices Drawer2

    The Sensory Devices Drawer.


      Technical Note: Many early computers had a row of toggle switches somewhere near the operator's control panel. There was a special computer instruction so that a program that was running could sense whether the switches were up or down, and for that reason they came to be called "sense switches".

    All of the items in the Sensory Devices Drawer are input devices. You can push the buttons. You can drag the handles of the sliders to new positions, or type into the little windows associated with the sliders. You can toggle the sense switches up and down. There are Wraith Scheme procedures that allow programs you write to obtain the results of your actions.

    The pushbuttons -- both the big red one and the four smaller ones below it -- can be set up to trigger a Wraith Scheme Interrupt when pressed. The pushbuttons have no default actions, though. There are no interrupts yet associated with them when Wraith Scheme starts up. They won't do anything until you arrange interrupts for them, but after you have done so, you can make the code for the interrupt -- the interrupt handler -- run whenever you like, by pushing the button.

    In contrast, the sense switches and the sliders are not set up to use interrupts. There are Wraith Scheme procedures that read their values, and your programs may use those procedures at any time, but there is no mechanism for informing a program automatically that the sliders or sense switches have changed. The idea is that you set those items the way you want them, and your program reads them when it reaches a point where it needs their values.

    Furthermore, there is no way for a program to move the sliders, or to flip the sense switches up and down. All the items in the Sensory Devices Drawer are input devices only: You use them to provide information or direction to a Wraith Scheme program.

    When Wraith Scheme starts running, the sliders are set at zero, and the sense switches are in the "off" position -- which is "down".

    There are procedures to obtain the value of each slider and to tell whether each sense switch is on or off. Each slider has a small window below it that shows its numeric value -- an integer in the range [0..255]. The slider setting and the value in the associated small window are connected automatically; no programming is required to make one change when the other does.

    Several procedures allow Wraith Scheme programs to access these devices. Example code for how to use the procedures follows the procedure descriptions. Remember that the MomCat and any kittens that may be present all have their own separate sensory devices drawers. Each of these procedures operates on the sensory devices of the Wraith Scheme process where the procedure is executed.

      (e::big-red-button-enabled?)
      

        Tells whether the big red button is set up to trigger an interrupt when pressed.

      (e::big-red-button-interrupt-number)
      

        Returns the number of the interrupt triggered by the big red button. Reports an error if the big red button is not enabled.

      (e::disable-big-red-button!)
      

        Disables the big red button.

      (e::disable-pushbutton! <pushbutton-number>)
      

        Disables the pushbutton whose number is given.

      (e::enable-big-red-button! <interrupt-number>)
      

        Enables the big red button, and sets it up to trigger the interrupt whose number is the argument.

      e::enable-pushbutton! <pushbutton-number> <interrupt-number>)
      

        Enables the given pushbutton, and sets it up to trigger the interrupt whose number is the second argument.

      (e::pushbutton-enabled? <pushbutton-number>)
      

        Tells whether the given pushbutton is set up to trigger an interrupt when pressed.

      (e::pushbutton-interrupt-number <pushbutton-number>)
      

        Returns the number of the interrupt triggered by the given pushbutton. Reports an error if that pushbutton is not enabled.

      (e::sense-switches)
      

        Returns an integer whose bits zero through seven (starting from the least significant bit) indicate whether the respective sense switches are on or off. A '1' bit indicates that the switch is on (up); a '0' bit indicates that it is off.

      (e::slider <slider-number>)
      

        Returns the value of the given slider, which will be an integer in the range [0..255].

    Here is some Wraith Scheme source code to demonstrate the operation of all of the sensory input devices just mentioned, as well as the level indicators that are described in the next section. This code will operate when only the MomCat is present. Once it has been executed, the pushbuttons will be set up to trigger interrupts that print messages to the MomCat's window. Every time you execute "test-sliders-et-al", the value of each level indicator will be set to the value of the corresponding slider, and the values of the sense switches will be printed out in binary.

      (c::set-interrupt-handler! 42 0
        '(display "AIIEEEE!!  You pushed the BIG RED BUTTON!\n")) 
      (c::set-interrupt-handler! 10 0
        '(display "You pushed button 0.\n")) 
      (c::set-interrupt-handler! 11 0
        '(display "You pushed button 1.\n")) 
      (c::set-interrupt-handler! 12 0
        '(display "You pushed button 2.\n")) 
      (c::set-interrupt-handler! 13 0
        '(display "You pushed button 3.\n")) 
      
      (e::enable-big-red-button! 42)
      (e::enable-pushbutton! 0 10)
      (e::enable-pushbutton! 1 11)
      (e::enable-pushbutton! 2 12)
      (e::enable-pushbutton! 3 13)
      
      (define (test-sliders-et-al)
        (e::set-level-indicator! 0 (e::slider 0))
        (e::set-level-indicator! 1 (e::slider 1))
        (e::set-level-indicator! 2 (e::slider 2))
        (e::set-level-indicator! 3 (e::slider 3))
        (display 
          (string-append
            "Sense switches: "
            (number->string (e::sense-switches) 2)
            "\n"))
         )
      


    Sensory Output Devices:

    The general rule is that all the items in the Wraith Scheme Instrument Panel are output devices: You use Wraith Scheme procedures to set their values, or Wraith Scheme sets them on its own, to provide status information. There is nothing there that you can press, drag, mouse on or type into, to make things happen in a Wraith Scheme program.


    Level Indicators:

    If you mouse the bottom of the Wraith Scheme Instrument Panel and pull down -- at the location shown by the arrow in the figure below, or anywhere on the bottom border of the panel ...



    Pull Here (on the instrument panel)

    Mouse and pull down at the location of the arrow.


    ... you will find some level indicators hiding above the nominal top of the instrument panel.

    Instrument Panel Enlarged

    The Wraith Scheme Instrument Panel, showing the level indicators at the top.


    The level indicators are output devices, whose values are under user control. From the viewpoint of Wraith Scheme, they are output devices -- you cannot read their values by means of Wraith Scheme procedures. Furthermore, there is no way to set their values by touching them or by typing into them; the values can only be changed by Wraith Scheme procedures.

    Each level indicator has a small numeric display to its left that shows its numeric value -- an integer in the range [0..1024]. The level indicator setting and the value in the associated numeric display are connected automatically; no programming is required to make one change when the other does. Just as for the sensory input devices, the MomCat and any kittens that may be present all have their own separate level indicators. The procedure to set level indicators affects the level indicators in the Wraith Scheme process where that procedure is executed.

    Here is the procedure to set level indicators.

      (e::set-level-indicator! <level-indicator-number> <integer>)
      

        Sets the value of the given level indicator to the integer which is the second argument.

    At the end of the preceding section is some Wraith Scheme code that demonstrates the operation of the level indicators and the sensory input devices. Here below is a procedure that will make each level indicator continuously display four times the value of the slider that has the same number.

      (define (track-sliders)
        (e::set-level-indicator! 0 (* 4 (e::slider 0)))
        (e::set-level-indicator! 1 (* 4 (e::slider 1)))
        (e::set-level-indicator! 2 (* 4 (e::slider 2)))
        (e::set-level-indicator! 3 (* 4 (e::slider 3)))
      (track-sliders))
      

    When the procedure is running, you can move the knob of any slider in the Sensory Devices Drawer, and watch the setting of the corresponding level indicator change in real time.


    Sense Lights:

    Wraith Scheme provides some "sense lights" -- in effect, light bulbs on the Wraith Scheme Instrument Panel that are under user control. There are eight of them. They are normally invisible and will remain so unless Wraith Scheme procedures are used to make them show up. When they are visible, they are lined up in a row near the top left corner of the Wraith Scheme Instrument Panel, as shown in the next figure, in which one sense light is set to be invisible.

    Sense Lights

    The Wraith Scheme sense light array, with most of the lights visible.

    The sense lights are numbered 0 through 7, from left to right. Each sense light may have one of eight illumination states, and those states are also numbered. In illumination state zero, the sense light is turned "off" -- it is visible, but resembles an unilluminated panel light, as in the second light from the right in the preceding figure. Illumination states 1 through 6 correspond respectively to the light being lit with the color red, orange, yellow, green, cyan, or magenta. In illumination state 7, the light is emitting darkness -- it looks like it is glowing black.

    The visibility of each sense light has nothing to do with its illumination state. If you make a visible sense light invisible, it retains whatever illumination state it previously had, and will reappear looking just the same after if you should make it visible again. Similarly, if you change the illumination state of a sense light that is invisible, the sense light does not show up -- it won't do that till you make it visible, but when it does it will have whatever illumination state you have just provided.

    I did not create names for the separate colors or illumination states; that is, there is no built-in Wraith Scheme object named "e::red" or anything like that. Feel free to define such auxiliaries if you wish.

    Procedures for working with sense lights include:

      (e::hide-sense-lights!)
      

        Makes all the sense lights invisible. Does not change the illumination state of any sense light.

      (e::rotate-sense-lights! <integer>)
      

        Circularly rotate the pattern of sense lights -- both illumination and visibility -- by the given integer. Positive integers rotate to the left. The rotation is done modulo the number of sense lights.

      (e::sense-light-number? <object>)
      

        Returns #t if <object> is an exact integer in the range legal for a sense light number, and returns #f otherwise.

      (e::sense-light-illumination-number? <object>)
      

        Returns #t if <object> is an exact integer in the range legal for a sense light illumination number, and returns #f otherwise.

      (e::set-sense-light-illuminations! <sense-light-illumination-state-number>)
      

        Sets the illumination state of all sense lights to the indicated state. Does not change the visibility of any sense light.

      (e::set-sense-light-n-illumination! <sense-light-number> <sense-light-illumination-state-number>)
      

        Sets the illumination state of the given sense light to the indicated state. Does not change the visibility of the given sense light.

      (e::set-sense-light-n-visibility! <sense-light-number> <boolean>)
      

        If the boolean is true (#t), makes the given sense light visible; if the boolean is false (#f), makes the given sense light invisible. Does not change the illumination state of the given sense light.

      (e::set-sense-lights-off!)
      

        Sets the illumination state of all sense lights to "off". Does not change the visibility of any sense light.

      (e::show-sense-lights!)
      

        Makes all the sense lights visible. Does not change the illumination state of any sense light.

    There is no way to read the illumination state or visibility of sense lights from Wraith Scheme.

    Note that "e::set-sense-light-n-illumination!" and "e::set-sense-light-n-visibility!" provide full control of sense light operation. The other procedures that affect illumination and visibility are for convenience.

    Procedures which change the illumination state or visibility of sense lights are relatively slow to execute. When speed is particularly important, make minimal use of sense lights.

    One of the source code example files provided via the "Source Code Examples" submenu of the Wraith Scheme Help Menu contains some demonstrations of the sense lights. To open that file in a browser window, use the "Sense Light Demonstration" menu item of that submenu. Cut and paste all the code in the file into Wraith Scheme, then try

      (demonstrate-sense-lights)
      

    You might also find

      (cylon)
      

    and

      (fuzz)
      

    interesting. They are in that same file. The latter two procedures loop forever, so you will have to reset Wraith Scheme to stop them.


    Top-Level Loop Variables:

    Like Common Lisp, Wraith Scheme maintains several variables that may prove handy should you forget to save an interesting input expression or output result. Their names are closely related to the names of similar variables used in Common Lisp.

      Common Lisp    Wraith Scheme
      ===========    =============
          -              >-<
          +              >+<
          ++             >++<
          +++            >+++<
          *              >*<
          **             >**<
          ***            >***<
      

    Wraith Scheme cannot use the exact same variable names as Common Lisp, because there is only one namespace in Scheme, and the symbols "+", "-" and "*" are already bound to the procedures for addition, subtraction and multiplication.

    If you use Wraith Scheme's parallel processing enhancements, you will find that each kitten has its own variant of these variables. See the parallel-processing section on Keeping Track of Things for details.

    When Wraith Scheme starts running, these variables are all initialized to the empty list.

      >+<
      >++<
      >+++<
      

        While a Scheme S-expression is being evaluated by the top-level loop, the variable >+< is bound to the previous form that was read by the loop. The variable >++< is bound to the previous value of >+< (that is, the form evaluated two interactions ago), and >+++< is bound to the previous value of >++<.
      >-<
      

        While a Scheme S-expression is being evaluated by the top-level loop, the variable >-< is bound to the S-expression itself; that is, to the value that will be bound to >+< once the current interaction is complete.
      >*<
      >**<
      >***<
      

        While a Scheme S-expression is being evaluated by the top-level loop, the variable >*< is bound to the result that was printed at the end of the last time through the loop; that is, to the value that was produced by evaluating the S-expression in >+< The variable >**< is bound to the previous value of >*< (that is, to the result from the form evaluated two interactions ago), and >***< is bound to the previous value of >**<.

    If the evaluation of >-< is aborted, then the values bound to >*<, >**<, and >***< are not updated; they are updated only if the printing of the value, returned from evaluating >-<, has begun.

    The utility of these variables somewhat overlaps the utility of the command-history and scrolling mechanisms available in the Input Panel and in the Main Display Panel.


    World Saves and Loads, and Stand-Alone Programs:

    Wraith Scheme has a mechanism to save the entire content of Scheme main memory, and some other things as well, in a special type of file, that may be reloaded into a running Wraith Scheme process at any future time, and reused. Many Lisp implementations provide such a mechanism; the process is traditionally called "saving and reloading a world", and the saved files are traditionally called "worlds".

    A saved world may be used as many times as you wish. Each time you load it, you will get back the state of Wraith Scheme when the world was created.

    Saved worlds can be loaded only into the specific version and release of Wraith Scheme from which they were originally saved. Furthermore, it is possible that for some reason a saved world may not "fit" into a particular Wraith Scheme process; for example, the saved world may require more memory or more kittens than the current Wraith Scheme process has. If you encounter such a problem, change the Wraith Scheme "Preferences" to provide more of the missing resource, then restart Wraith Scheme.

    You may load and reload any sequence of saved worlds into any running instance of Wraith Scheme, as long as each of them fits, but each time you load a world it will overwrite the entire content of the previous world. There is no way to "merge worlds".

    It usually makes sense to save and load worlds only from top-level in Wraith Scheme, not from the middle of programs that are running: A saved world does not preserve the contents of Wraith Scheme's stack or continuation, or any of the other data that would be required to pick up in the middle of an executing program. Thus a saved world contains only a top-level environment: Once that environment is reloaded, you must evaluate an expression manually, or arrange that Wraith Scheme run a procedure automatically upon loading the world, to get things to start happening.

    When a world is loaded, Wraith Scheme finds itself running at the top-level loop, even if the world was saved from within a program, or even if you called the procedure to load it from within an executing program.

    Wraith Scheme provides a mechanism to store within a saved world a procedure which will automatically be executed at top level immediately after the world is loaded. By using this mechanism, you can make the act of loading a Wraith Scheme world not only load a particular set of procedures and data into Wraith Scheme, but also start something happening: A saved world becomes something like an autonomous Scheme program, which starts running by itself as soon as it is loaded. That mechanism is described below, in the subsection about "e::main".

    Saving a world does not cause Wraith Scheme to stop running. You can continue using a Wraith Scheme process after saving a world, just as if you had not saved the world in the first place.

    You may load a Wraith Scheme world by using the "Load World" command from the Interpreter Menu, or by using the "e::load-world!" procedure described below.

    You may save a Wraith Scheme world by using the "Save World" command from the Interpreter Menu, or by using the "e::save-world" procedure described below.

    Procedures associated with Wraith Scheme world operations include:

      (e::main)
      

        The procedure "e::main" is not defined by Wraith Scheme; indeed, the symbol "e::main" does not even exist in the distributed version of Wraith Scheme! It is you who may, if you wish, define "e::main". You may make it be any any lambda expression of no arguments that you happen to be fond of.

        If you save a world in which "e::main" is a procedure of no arguments, then whenever that world is loaded into a Wraith Scheme process, "e::main" will be executed immediately after the world has loaded. If you are running parallel Wraith Scheme processes -- more than one kitten -- only the MomCat will execute "e::main".

        Use of "e::main" makes it possible to start something happening automatically in a newly-loaded world.

        Wraith Scheme will report an error if it loads a world in which "e::main" is bound to a lambda expression that requires one or more arguments. The error will be reported after the world load has completed, and the loaded world will then otherwise be ready for use.

        If Wraith Scheme loads a world in which "e::main" is bound to a Wraith Scheme object that is not a lambda expression, it will do nothing with the bound value; in particular, it will not evaluate or print out that value. The world load will then be ready for use.

          Technical Note: If you are familiar with the programming language C or C++, you might think of "e::main" as analogous to "main". When the executable binary file for a C or C++ program is loaded into memory, "main" starts executing automatically -- if it did not, the loaded binary would just sit there, doing nothing. So it is with Wraith Scheme worlds and "e::main".

      (e::load-world! <string naming a file>)
      (e::load-world!)
      

        Only the MomCat may execute this procedure.

        Loads a "saved world" file, which contains most of what was known to Wraith Scheme when the file was created. If no file name is given, puts up a standard dialog box for selecting a file, and also displays a message telling what world was loaded. Returns #t. Returns control to the top-level loop after the world is loaded, even if the procedure was called from deep within another procedure.

        This procedure overwrites everything in Wraith Scheme's memory!

        In particular, if you load a world from within a Scheme program, the program will stop executing immediately after the load. If you use the "load" procedure to load a file of Scheme code, and that file loads a world, then nothing more will be read from the file after the world load, because the "load" procedure itself will have stopped.

      (e::save-world <string naming a file>)
      (e::save-world)
      

        Only the MomCat may execute this procedure.

        Creates a special "saved world" file, that contains almost everything known to Wraith Scheme when the file is created. If no file name is given, puts up a standard file dialog box for selecting a file. Returns #t. This procedure does not overwrite any objects.


    Easter Eggs:

    In computer slang, an "Easter Egg" is a program feature that is cute or useful but perhaps temperamental or obscure. "Obscure" means that the feature is poorly documented or even not documented at all, and is unlikely to be stumbled upon by chance. There are likely to be features that fit this definition in every release of Wraith Scheme, if only for the following reasons:

    • I may have put in a feature to aid in the development and release process itself -- something that only a developer would love or use.

    • I may have been working on a new feature, to be explained and documented subsequently, that wasn't quite ready when I decided to release a new version of Wraith Scheme.

    • I like cute.

    You might wonder why I don't just disable such features for a release. The reason is that disabling one part of a program often breaks another. It's kind of like do-it-yourself plumbing repair, or maybe like trying to pull a "loose thread" off a garment you are wearing: The consequences may be disastrous. Wraith Scheme will be much more stable if I just leave such features in.

    Anyhow, here are some hints about some -- but not all -- of the Easter Eggs in Wraith Scheme.

    • Running Wraith Scheme from a Unix Shell:

      This feature will make sense only to those of you who know how to use a Unix shell. Furthermore, this feature may not be as useful as you are hoping for: Mostly, it provides a way for Wraith Scheme to load and execute files of Scheme code under control of Unix shell scripts and the like. If that sounds useful, keep reading ...

      To run Wraith Scheme from a Unix command line, open a window of the "Terminal" application, and enter the path to the Wraith Scheme executable. The problems are finding the actual executable, and figuring out how to use a Unix path that contains blank spaces. Let me remind you how to do those things.

      The actual Unix executable for Wraith Scheme is buried deep within the "application" -- the thing with the Wraith Scheme icon -- that shows up in Macintosh Finder windows. (That's how Macintosh applications work, it's not just something weird that I put in.) For example, suppose you have placed the Wraith Scheme application in your "Applications" folder. Then the full path to the Unix executable, presented here with sufficient quotation marks for the Unix shell to understand it, is:

        /Applications/"Wraith Scheme.app"/Contents/MacOS/"Wraith Scheme"

      So what you would type at the command prompt, in a Macintosh Terminal window, using the default shell, to get Wraith Scheme going, is

        /Applications/"Wraith Scheme.app"/Contents/MacOS/"Wraith Scheme"

      All that does is start the program going, just as if you had clicked on its icon in the Finder, but there's more: Wraith Scheme has a command-line flag that makes it use the Terminal window for input and output. The flag is "-t". (There are some more flags; I will get to them in a few paragraphs.) At the command prompt, enter:

        /Applications/"Wraith Scheme.app"/Contents/MacOS/"Wraith Scheme" -t

      The usual Wraith Scheme window will open, but all the text that Wraith Scheme would normally have printed to the Main Display Panel will instead be printed in the terminal window, and instead of entering Scheme commands into the Input Panel you will need to type them into the terminal window.

      Unfortunately, the "-t" flag does not quite turn Wraith Scheme into a program that behaves well in a Unix shell. There are several gotchas:

      • When you start Wraith Scheme from a Unix prompt, you must provide the full Unix path, as shown in the examples above. Relative paths will not work -- Wraith Scheme will run, but it won't be able to find the world that it is supposed to load at startup. (You could load the world yourself, if you wished, but it is probably easier just to type the full pathname.)

      • Output to the terminal shell will be a bit garbled -- there won't be enough "return" characters sent to the shell for everything to look tidy. The results of running a Scheme command will probably start on the same line where the command ended, instead of starting on a new line.

      • You will have to use the regular Wraith Scheme window for anything you can't do from the Input Panel. Most notably, that includes resetting to the top-level loop. It also includes any use of the Dialog Panel or the Message Panel.

      I created this feature so that I could run Wraith Scheme and load files of Scheme source code from a Unix shell script or makefile. I have thereby automated testing of Wraith Scheme. My test scripts consist of a long series of shell commands or makefile commands that look something like this:

        echo "(load <path to a test file>)" | /<path to the executable>/"Wraith Scheme" -t <more flags>

      All that does is start Wraith Scheme running, with whatever additional command-line flags are provided, and send it the single command:

        (load <path to a test file>)

      (Note that the Unix "echo" command adds the necessary "newline".)

      My files of test code use the Scheme "transcript-on" and "transcript-off" procedures to record output in a file; those work fine -- output is not garbled -- with the "-t" flag.

      Other flags useful in connection with "-t" are:

      • -c < 0 or 1 > -- for 1, set the internal Wraith Scheme compile-defines flag; for 0, clear it. If the "-c" option is not used, the value of the flag is taken from the Wraith Scheme preferences.

      • -m <integer> -- set the Wraith Scheme main memory size to the number of MByte specified by the integer. That is, "-m 20" indicates a main memory size of 20 MByte. If the "-m" option is not used, the memory size is taken from the Wraith Scheme preferences.

      • -g <integer> -- set the Wraith Scheme generational garbage collector generation size to the number of MByte specified by the integer. That is, "-g 2" indicates a generation size of 2 MByte. A value of zero turns the generational garbage collector off. If the "-g" option is not used, the generation size is taken from the Wraith Scheme preferences.

      • -u < 0 or 1 > -- tell Wraith Scheme whether or not to use the generational garbage collector: A '1' means "use it", and a '0' means "do not use it." The "-g" flag will be ignored if the "-u" flag is followed by zero.

      • -z <integer> -- create the number of kittens specified by the integer. The number specified does not include the MomCat; for example, "-z 3" causes Wraith Scheme to run with four processes -- a MomCat and three kittens. If the "-z" option is not used, the number of kittens is taken from the Wraith Scheme preferences.

    • Meow:

      There is this little panel at the top right corner of the Wraith Scheme window ...

    • Unusual Icon Appearance:

      Now and then the Wraith Scheme icon may look a little different ...

    • Cat-Like Behavior:

      Every so often, Wraith Scheme may undertake some harmless cat-like behavior ...

    • Pig Latin:

      Cats are fond of Roman numerals ...

    • Like A Rainbow:

      Long ago, in a Macintosh far, far away ...


    Bugs, Flaws, Limitations, and Dealing with Them:

    I am sure Wraith Scheme contains bugs, and I want to hear about them so that I can fix them.

    I am particularly worried about the possibility of bugs that are platform dependent, in that they occur on some combinations of Macintosh hardware and operating system and not on others. I do not have a warehouse full of different Macintosh computers to test Wraith Scheme on, so it is logically conceivable -- and it has in fact happened -- that I might release a version of Wraith Scheme containing a bug that I am not equipped to discover.

    So by all means, do send in bug reports.

    My EMail address is Jay_Reynolds_Freeman@mac.com.

    If you ever encounter a fatal or non-fatal error with a message like

      "Implementation error ..."
      

    or

      "Implementation: ..."
      

    then I would much appreciate a bug report with as many details as you can provide. (In particular, include the entire message.) Such errors indicate that I have inadequately guarded against some anticipatable problem: It's my fault; I will be eager to do better.


    Known Bugs and Flaws:

    The only reason I will release a distribution of Wraith Scheme containing a known bug or a glaring flaw is that there is some reason I cannot fix the problem. I will risk tempting fate by stating that at present, I have none to report.


    Limitations:

    • Read and Print Overflow:

      Although Wraith Scheme can store large and complicated data structures in its main memory, it cannot necessarily read them in all at once: Wraith Scheme can read lists that are very long. In testing, I have loaded files whose text included lists of 100 000 items, something like:

        '(item-1 item-2 ... item-99999 item-100000)
        

      However, lists that are too deeply nested will cause Wraith Scheme to crash. Such a list might be

        '((( ... ((( some-list-item ))) ... )))
        

      in which the "..." stands for tens of thousands of parentheses of the appropriate kind

        Technical Note: The problem is stack overflow in the underlying routines, which are written in C. When the reader reads lists, it iterates in the "cdr" direction, but does recursive (and non-tail-recursive) calls in the "car" direction.

      There is a similar problem in the routines that print long or deeply-nested lists, but I have not tested to determine its extent.

    • Quitting When Garbage Collection Is Happening:

      Wraith Scheme will crash if a kitten exits while garbage collection is in progress. Therefore, if you try to quit from Wraith Scheme (via the "Quit" menu item and the like) while garbage collection is happening, Wraith Scheme will wait till it has finished before quitting. Normally, that is not a problem, but if you are using a large Scheme memory, it may take a long time before Wraith Scheme actually quits. Worse, if there should be some kind of failure during garbage collection (and that would be a serious bug -- let me know about it if it happens), it may be impossible to quit from Wraith Scheme by the usual means. In that case, remember that Apple provides a "Force Quit" command for every application, which you can find by holding down the "option" key while clicking on the application's icon in the dock.

    • Numerical Exactness:

      Wraith Scheme relies in part upon built-in features of macOS and of Macintosh hardware to determine whether or not the result of a floating-point arithmetic calculation is exact; this term refers among other things to what the standard Scheme procedures "exact?" and "inexact?" return. These built-in features vary in capability from one version of macOS to the next, and from one kind of Macintosh hardware to another.

      The main consequence here is that sometimes you might expect that a floating-point calculation would return an exact result, but the procedure "exact?" doesn't say that it did. Furthermore, what "exact?" has to say about the result of any given calculation may vary depending on what kind of Mac and which version of macOS you are running.

      This mildly inconsistent behavior is not technically a Wraith Scheme bug, since the R5 standard is rather lenient about requiring Scheme implementations to return exact results whenever possible, but it bugs me, so I thought I would report it here.

    • Numerical Accuracy:

      The floating-point algorithms used by the Macintosh may differ from one version of macOS to the next and also from one kind of processor hardware to the next. Thus the numerical results returned by Wraith Scheme floating-point calculations may vary depending on which version of macOS you are running and on what kind of Macintosh you have, even if you perform the same calculation, using the same input data. The differences are typically very small -- down in the least significant digits of the result -- but they are there. The trigonometric and inverse-trigonometric routines are where I have most often noticed such differences.

      The bit about "kind of processor" used to matter for earlier versions of Wraith Scheme, which could run both on Macintosh computers with Intel processors and on those with PowerPC processors, and now matters for later versions of Wraith Scheme, which can run on Macintosh computers with Intel processors and on those with "Apple silicon" processors, that are based on the Arm architecture.


    Common Problems and Solutions:

    Here are a few hints about problems that may happen when you try to run Wraith Scheme:

    • Wraith Scheme crashes as it is starting up, without getting far enough emit any messages of its own.

      Make sure you have met the system requirements for Wraith Scheme. If that doesn't help, you might want to try some of the steps listed in the "Elementary Debugging" section. Or you might just want to send me a bug report.

      Note in particular that versions of Wraith Scheme up through version 2.25 will not run on Macintosh computers with "Apple silicon" processors, not even under the "Rosetta 2" emulation system.

      Check the MacIntosh Console Log for error messages from Wraith Scheme: Wraith Scheme may put error messages there if it encounters a problem early in starting up, before Wraith Scheme's own error-reporting mechanism has gotten going. The console log is somewhere in the "Console" folder of the "Logs" folder of your computer's "Library" folder. On my Macintosh, the Unix path to the console log I look in is "/Library/Logs/Console/501/console.log". (The "Console" folder is organized so that there is one console file for each user who has an account on your Macintosh -- the "501" in the Unix path refers to the folder with the log for the user account I use on my own Macintosh when I am using Wraith Scheme.)

      The way macOS handles error messages has changed over time, and I have modified Wraith Scheme's error-handling system to catch up: On versions of Wraith Scheme past 2.25, I have installed a mechanism to record certain fatal error messages in a text file in the Unix "/tmp" directory. The full path to that file is "/tmp/WraithSchemeFatalErrorLog.txt". Wraith Scheme will create that file if it does not exist, and will append new error messages to the existing file if there already is one. If you like, you may delete the file, and Wraith Scheme will create a new one when needed.

    • Wraith Scheme complains about insufficient memory.

      Try running Wraith Scheme with less memory requested. See the section, Changing the Startup Defaults.

    • Wraith Scheme takes forever to initialize.

      The likely cause of this problem is that Wraith Scheme is trying to initialize a large main memory, and that does take a while. As a rule of thumb, if the main memory size you have requested exceeds a quarter the size of the physical memory (RAM) installed in your Macintosh, initialization delays may be excessive. The main fix here is just to wait, though it may help if you shut down other applications.

      If you don't want to wait, you may force Wraith Scheme to quit via the special menu that appears when you option-click on the Wraith Scheme icon in the dock. If you want Wraith Scheme to forget about trying to use a large memory in the future, find the Wraith Scheme preferences file and delete it, and Wraith Scheme will create a new preferences file with default preferences the next time you start it up:

      The preferences file will be named "com.JayReynoldsFreeman.WraithScheme.64.plist" (yes, it will have my name, not yours), and it will be located in the "Preferences" folder of the "Library" folder of your home folder.

        Technical Note: If you are willing to be bold, you may edit that preferences file by double-clicking on its icon. That will only work if your Macintosh contains an application -- like Xcode -- that can open a preferences file. Once it is open, click on the small triangular icon near the word "Root", locate the "MainMemorySize" item, click on the number associated with it -- it is in megabytes -- and change it to however many megabytes of memory you want. If things get messed up, just delete the whole file.

      The reason you have to modify or delete the preferences file in this circumstance is that when Wraith Scheme is initializing memory, it hasn't yet gotten far enough in starting up so that you can just open the preferences window and change the memory size in a more normal way.

    • Wraith Scheme takes forever to garbage-collect.

      The likely problem is that Wraith Scheme is using a large main memory, in which garbage collection simply takes a long time.

      The speed of garbage collection will vary from fast -- when the Wraith Scheme main memories are contained entirely within physical memory chips -- to very slow -- when the Wraith Scheme main memories are in part stored as virtual memory, on disk. It takes a long time to "swap"; that is, to move data back and forth between disk and physical memory. On a 2006 model Macbook, with a 2 GHz Intel Core Duo processor and 1 GByte of memory, with no other user processes but Wraith Scheme running, with a Wraith Scheme main memory size of nearly 1 GByte, with a substantial portion of main memory containing non-garbage, I have seen garbage collection take as long as twenty minutes. More modern Macintosh computers are enormously faster, but that means you can use more memory, and garbage collection will again be slow: On a 2021 Mac Mini, using Apple's "M1" processor, with 8 cores and 8 GBytes of physical memory, I once ran Wraith Scheme with a Scheme main memory size of 64 GBytes, and when that filled up, garbage collection took half an hour.

        Technical Note: Most of the swapping takes place early in the garbage-collection process, and I have no way to monitor the progress of swapping, so there isn't much point in using a progress indicator to show what is going on; it would just sit at a fixed position for a long time, then rapidly zoom through the rest of its range.

    • You try to type something in the main window, and nothing happens -- no text appears anywhere.

      Have you clicked in the Input Panel, to select that area for typing? The Input Panel is normally the only part of the Wraith Scheme window that will accept typing: If your last mouse click in the Wraith Scheme window was anywhere else, anything you type will be ignored.

      Input Panel

      The left end of the Input Panel.

    • You get error messages suggesting that such basic primitives as "define", "if", "let", "cond" and "begin" are undefined.

      These basic primitives, as well as several others, are macros that are only available in the saved world that comes with Wraith Scheme, or in worlds that you have made from it. See the section, Wraith Scheme Startup Actions.

    • You aren't quite sure you know enough Lisp or Scheme to operate Wraith Scheme properly.

      See the Scheme References and Lisp References sections. In order from simplest to most complicated, I recommend (1) Friedman and Felleisen, (2) Springer and Friedman, and (3) Abelson, Sussman and Sussman. And do get a copy of the "R5" report: You will need it sooner or later.

      Or, try an Internet search.

      Don't forget the help files available via the Wraith Scheme Help Menu.

    • You are tired of seeing the demonstration program when Wraith Scheme starts running.

      Open the "Preferences" panel via the Wraith Scheme menu, clear out the text in the window after "Load This Source File After Startup", and press the "Accept These Preferences" button. (Note that the "Choose" button in that panel is to allow you to select another Scheme program to run when Wraith Scheme starts running, if you should wish to do so.)

      Typical Preferences Window

      The Preferences Window, with "Load This Source File After Startup" cleared.

    • The fonts are too small to read comfortably, particularly with that silly yellow background.

      Look in the Font Menu for commands to change font size, text color, and background color. And hey, I like the yellow background. But if you do not, there are many alternatives ...

      Colorful Window with Big Font


      Pastel Window with Small Font

      Font sizes and colors to suit every mood...



    • You can't figure out how to create programs and get them into Wraith Scheme.

      Use any programming editor, text editor, or word processor you like, to create a file containing your source code. Save it as a text document -- Scheme requires ASCII text. Use Wraith Scheme's "Load File" command (in the Interpreter Menu) to load the file, then do what you will with the things your file defines.

      If you are debugging code, Wraith Scheme has the commands "Reload Last File" and "Load Recent File..." (all in the Interpreter Menu, and there is a button for "Reload Last File" in the Basic Buttons Drawer) which allow you to get at recent files quickly, without fussing with a browser or a long path name.

      There is also the "Automatically Reload Last File After You Save Changes" command in the Interpreter Menu (and there is a button in the Basic Buttons Drawer) that does the same thing). When you check this command, or push the button in, Wraith Scheme will look at the last file you have loaded every few seconds, to see whether you have updated it. If so, Wraith Scheme will reload the file automatically -- in essence, Wraith Scheme will push the "Reload Last File" button for you, whenever you update the file. That way, you can make changes to the file using your favorite editor, save them, and have the newly-changed file loaded by Wraith Scheme automatically, so that your changes will be ready to test when you switch to the Wraith Scheme window.

      Letting Wraith Scheme load files this way allows you to create and edit Scheme source-code files using the editor you prefer, instead of using some built-in editor that I happened to like.

    • Compiling takes forever.

      Wraith Scheme's internal compiler is indeed fairly slow, and I have no present fix for that problem. Remember that you can turn the compiler off, via the "Compile Defines" menu item in the Interpreter Menu, or by means of procedures described in the State Flags section. Also, be advised that the compiler is much happier dealing with a large number of small procedures than with a small number of large ones.

    • You can't keep all these stupid parentheses balanced!

      Welcome to the club. Many programming editors have parenthesis-matching features; I happen to like EMacs, which is no longer provided with Apple's "Xcode" development environment but can be downloaded from various open-source repositories.

      Wraith Scheme itself will provide cues about missing parentheses and missing double-quotes when you are entering text directly into the Input Panel: Open the Basic Buttons Drawer, type an expression that doesn't have enough right parentheses -- something like

        (+ 2 2
        

      -- and then press "return". A little text panel will appear at the bottom of the Basic Buttons Drawer to remind you about the missing right parenthesis, like this:

      Expect Right Parenthsis

      For a missing quotation mark, the panel looks like this:

      Expect Quotation Mark


    Elementary Debugging:

    If you encounter a problem while running Wraith Scheme, by all means send me a bug report.

    You might want to do a few things on your own before contacting me, both to save your time if the problem turns out not to be Wraith Scheme's fault, and to help me identify and fix any bug that may be present.

    Try to pin down what it takes to make the problem happen, as accurately and completely as you can. Unreproducible bugs are almost impossible to fix. They are like the rattle in your car that goes away when you take it to a mechanic, or the things that go bump in the night but are not there when the sun rises.

    Here are some things to do, that might help pin down a problem:

    • If the problem happened when you were running code that you had compiled, try again without compiling the code. That is, turn off the "Compile Defines" option, reload your code without applying "e::compile-form" to any of it, and try again. If the problem does not recur, or recurs in a different way, it may be that the difficulty is with the compiler itself.

    • Reboot your Macintosh and try again.

    • Get a fresh copy of Wraith Scheme and try again.

    • Carefully consider the possibility of a computer virus or similar software abomination. There are no general rules to detect these: No two are alike. Magazine articles, Internet searches, user groups and dealers can help you learn about the latest viruses.

    • See if you can duplicate the problem on another Macintosh. If so, then you will know that your own computer's hardware and software are not at fault: Perhaps the fault is mine, and you should send me a bug report.

    If you do send me a bug report, give your best description of the problem and of what I must do to make it recur for investigation. State what kind of Macintosh you used, how much memory it had, what version of the Apple System Software you used, and whether you were using the compiler. If you suspect a problem with Wraith Scheme's interaction with some other software, tell me about that, too.

    It will help me a lot if you do these things and describe what happened, and you may be able to save yourself some time if the problem turns out to be one you can fix.

    I may not be able to do much about your bug, though, if I do not have the same kind of Macintosh and the same software that you do: If the problem turns out to have something to do with hardware or software that I do not have, I am unlikely to be able to reproduce it, much less fix it.


    Development Testing:

    This section describes the tests I perform before releasing a version of Wraith Scheme. I include it not to boast about how much testing I do, but to apologize for how little. I want you to be able to read this section if you encounter a problem, and learn immediately whether or not you are using Wraith Scheme in a way I have tested. Don't waste time trying to cope with problems I have had no opportunity to ferret out: Just send a bug report.

    Wraith Scheme was originally developed on a 13-inch Macbook with a gigabyte of memory and an 80-gigabyte hard drive, running macOS version 10.4.6 and later. I later acquired a 2008 model Mac Pro, initially running macOS version 10.6.1 but subsequently upgraded, and used that for many years. Now I have a 2019 Mac Pro running macOS 11.6, which I use for development and testing of Wraith Scheme.

    I have a test suite comprising millions of Scheme S-expressions, which I run frequently. In general, the tests operate by calling either "load" or "e::set-input-port!" on various files, or by redirecting I/O via command-line options, and record output on transcript files, for subsequent comparison with files that contain correct output for the tests. My suite attempts to exercise each built-in function or macro of Wraith Scheme in at least a few interesting cases, to create every non-fatal error condition that Wraith Scheme can detect, and to perform many tests of Wraith Scheme's numeric input and output routines.

    Prior to releasing Wraith Scheme, I run the test suite repeatedly, to verify that Wraith Scheme does not crash and continues to produce correct results.

    Not all of the features of Wraith Scheme can be exercised by evaluating expressions: I use a list of manual operations to test Wraith Scheme Instrument Panel commands, Dialog Panel interactions, and so on.

    None of this should impress you. I have worked professionally in software/hardware testing, where I have seen person-years of effort and machine-years of computer time spent on testing and debugging systems simpler than Wraith Scheme, without finding all the bugs. I have made only a small fraction of that effort testing Wraith Scheme.

    Furthermore, I have little access to other computers for testing.

    I would appreciate additional reports of successful use of Wraith Scheme on Macintosh configurations or types that I have had no opportunity to test. I will be particularly eager to hear of any bugs in Wraith Scheme that appear to relate to circumstances that I did not test.

    Notwithstanding the formal test suite, probably the most important testing I do is to use Wraith Scheme myself. I have written lots of macros and a compiler in Wraith Scheme: They or the code they generate are part of the distributed world loads, and are exercised in even the simplest Wraith Scheme programs. I have used a checkbook-balancing written and run in Pixie Scheme to manage my own checkbook, and the bank hasn't come after me yet. (That program has been ported to Wraith Scheme, and is available via the "Source Code Examples" menu item of the Help Menu.) I have written or ported a good deal of other software, and it all seems to operate correctly. That's encouraging. But the ultimate test is not whether I find any more bugs in Wraith Scheme, but whether you do. Let me hear from you when that happens.


    Timeline:

    What's New:

    This section summarizes what is new in Wraith Scheme since the previous release. I omit cosmetic improvements, minor changes in documentation, and minor changes in displays.

    Items are listed in more or less the order in which I dealt with them.

    The current release is 2.27, the sixteenth release of Wraith Scheme as a 64-bit application, and the sixteenth release under the GNU General Public License.

    • Added new procedures "e::test-and-clear!" and "e::test-and-set!", for lockless coding by the user, described in Locks and Critical Sections.

    • Changed the keyboard equivalent for the "Interpreter" menu item for "Reset to Top-Level Loop" to "option-shift-command-delete". It used to be "control-option-command-delete", but Apple made system-wide changes with the launch of macOS Monterey that prevented that equivalent from working.

    • Substantial rewrite of the portions of Wraith Scheme that parse and print numbers, and of many operations involving numbers. Fixed several minor bugs, and one major one ...

    • Bug Fix: Numeric parsing previously had the order of the sign ('+' or '-') and the exact or inexact prefixes ("#e", "#E", "#i" and "#I") wrong -- the latter are supposed to precede the former.

    • Wraith Scheme no longer supports "embedded sharps" in numbers, described in R5 Section 7.

    • Added "call/cc" as a synonym for "call-with-current-continuation".

    • Installed an "allocate cache" mechanism to increase speed of consing in Wraith Scheme processes with more than one kitten. Allocate caches are described in more detail in the Wraith Scheme Internals document.

    • Started putting things in the Wraith Scheme Attic, described in The Window Menu.


    What Used to be New:

    This section summarizes changes in Wraith Scheme between its first release and the last release but one. It includes changes in the earlier, 32-bit application up to the time I started work on the 64-bit version, but omits bug fixes and small updates made to the 32-bit version since that time. I also omit mention of cosmetic improvements, minor changes in documentation, and minor changes in displays.

    Release 2.26 was the fifteenth release of Wraith Scheme as a 64-bit application, and the fifteenth release under the GNU General Public License.

    • Made Wraith Scheme operate on both Macintosh computers using Intel processors and those using Apple arm-like proprietary processors ("Apple silicon").

    • Made Wraith Scheme support different maximum Scheme memory sizes on Intel processors and Apple silicon processors -- 800 MByte on the former and 64 GByte on the latter. I hope that's enough.

    • Changed the name of function "e::string-speak" to "e::string->speech", which seemed more in the Scheme tradition.

    • Added a new "drawer" (not the former Apple implementation), called the "Attic", above the top of the Wraith Scheme main window, accessed via a menu item in the window menu. This feature has no present purpose, and is only documented for the sake of completeness. I expect to make use of it in future releases of Wraith Scheme.

    • Bug Fix: Procedure "closed-port?" did not always report that ports were closed.

    • Bug Fix: Procedures "e::kitty-x", "e::kitty-y" and "e::kitty-heading" were mixed up: Each produced the output of one of the others.

    • Bug Fix: Substantial rewrite of procedures using forwarding pointers (which are described in the Wraith Scheme Internals file. Basically, I was using more of them than necessary ...

    • Added new procedure, "e::push-list-into-continuation", described in the section on Evaluation.

    Release 2.25 was the fourteenth release of Wraith Scheme as a 64-bit application, and the fourteenth release under the GNU General Public License.

    Release 2.24 was the thirteenth release of Wraith Scheme as a 64-bit application, and the thirteenth release under the GNU General Public License.

    • Bug Fix: Procedure "e::inspect" did not work properly on ports, and sometimes caused a crash. Fixed.

    • Bug Fix: Several arithmetic operations crashed when presented with inputs that were numeric constants in the form of fractions that represented infinities or nans; such constants include 1/0, -1/0, 0/0, and the like. Fixed.

    • Bug Fix: Pressing the "meow" button near the top left corner of the Wraith Scheme window caused Wraith Scheme to hang in an infinite loop in both Lion (macOS 10.7) and Mountain Lion (macOS 10.7). (The same source code had produced correct behavior in the last several preceding versions of macOS. Go figure ...) Fixed.

    • Added some more procedures for dealing with evaluation.

    • Added a file-based package system, and procedures to use it.

    Release 2.23 was the twelfth release of Wraith Scheme as a 64-bit application, and the twelfth release under the GNU General Public License.

    • Bug Fix: Procedure "values" did not accept zero arguments; that is, "(values)" produced an error message. Fixed.

    • Added an implementation of something very similar to turtle graphics, that last being a simple drawing system that Seymour Papert installed in the Logo programming language in the late 1960s. See the section on Kitten Graphics.

    Release 2.22 was the eleventh release of Wraith Scheme as a 64-bit application, and the eleventh release under the GNU General Public License.

    • Added support for rational numbers in which the numerator and denominator are stored separately, plus some related auxiliary procedures as enhancements. See R5 Section 6 for details. Also see the section on Long Ratnums and Continued Fractions.

    Release 2.21 was the tenth release of Wraith Scheme as a 64-bit application, and the tenth release under the GNU General Public License.

    • Numerous small changes to the user interface: Buttons and menu items relabeled, and a few menu items removed, et cetera.

    • Kittens no longer have preferences; the MomCat's preferences are used for the kittens, where applicable. (That is "where applicable" because the kitten text colors and background window colors cannot be changed, so the MomCat's preferences do not apply in those cases.)

    • Added new procedures e::select, e::reduce, and e::make-integer-range, all described in the section on Miscellaneous Procedures.

    • Added new procedures related to sorting and merging.

    • Bug Fix: Internal definitions would not nest; that is, if an internal definition contained another internal definition, the code would not work correctly. Fixed. (See the R5 report, section 5.2.2, for "internal definitions".)

    Release 2.20 was the ninth release of Wraith Scheme as a 64-bit application, and the ninth release under the GNU General Public License.

    • Added complex numbers with non-zero imaginary part, implemented according to the R5 report.

    • Added new predicate "e::long-complex?", for identifying numbers stored in a new, implementation-dependent format that supports complex numbers with non-zero imaginary part.

    • Added help for finding out the syntax of procedure and special form use while typing; that is, for finding out what types of arguments are required, and in what order. See the section on Command Syntax Information, or just try pressing "option-escape" when you have typed the name of a built-in procedure or special form.

    • Changed the arrangement of sliders and of level indicators, in the Sensory Devices Drawer and the extended Instrument Panel, respectively.

    • Removed the "Previous Command" and "Subsequent Command" buttons from the Basic Buttons Drawer.

    • Added a lot of tutorials about various aspects of Scheme in general and Wraith Scheme in particular. Added the Tutorials Menu to the menu bar. Tutorials vary in content, and will probably increase in number with subsequent releases.

    • Bug Fix: Required R5 procedure "string" was missing. Added it.

    • Rewrote the section of Wraith Scheme that actually performs evaluation of Scheme expressions.

    Release 2.15 was the eighth release of Wraith Scheme as a 64-bit application, and the eighth release under the GNU General Public License.

    • Added pushbuttons, sliders, sense switches and level indicators, for user-programmable input/output. See the sections on the Sensory Devices Drawer, on Sensory Input Devices, and on Sensory Output Devices.

    • Bug Fix: Fixed an interesting bug that caused Wraith Scheme to burn way more processor cycles than necessary while inactive. This bug did not slow the program down measurably when the program was doing something, but it may have cause other programs to slow down when Wraith Scheme was inactive, by depriving them of cycles.

    Release 2.14 was the seventh release of Wraith Scheme as a 64-bit application, and the seventh release under the GNU General Public License.

    • Added means to run an applicative subset of Wraith Scheme (but use caution, "applicative" means different things to different people).

    • Bug Fix: Fixed a bug that produced erratic scrolling of the Wraith Scheme input panel when you had used the command history mechanism to scroll back to a previous command, and then were using the left and right arrow keys to move the insertion point and typing cursor around in that line: The input panel would lose track of which line it was supposed to display, and would toggle back and forth between the line you had selected and the line just above or below it.

      This problem only occurred when the user option "Use smooth scrolling" was selected: That option is controlled by a check box in the "Appearance" pane of the "System Preferences" application.

      This problem may have been the actual cause of the "hardware-dependent" bug reported fixed in the release of Wraith Scheme 2.10 (discussed below). I still worry about bugs that only show up in particular combinations of hardware and software.

    Release 2.13.1 was the sixth release of Wraith Scheme as a 64-bit application, and the sixth release under the GNU General Public License.

    • Bug Fix: Fixed an error in specifying the address of Scheme main memory (shared by the MomCat and all kittens).

    Release 2.13 was the fifth release of Wraith Scheme as a 64-bit application, and the fifth release under the GNU General Public License.

    • Added a non-printing object, which may be typed in as "#n". Also added a procedure, "e::non-printing-object?" to test whether any Wraith Scheme object is a non-printing object. See the discussion here.

    • Added some sense lights to the Wraith Scheme Instrument Panel.

    • Bug Fix: Fixed a minor compiler bug, whose main symptom was that some of the compiler functions could not be nested. That is, for example,

        (e::compile-form (e::compile-form (lambda ... )))
        

      did not always produce the correct result. (The outermost "e::compile-form" should have had no effect, but sometimes it did.)

    • Bug Fix: Arranged that path names saved for use in the various menu commands that load files are full absolute paths, not relative ones, so that they will work no matter what the current working directory is.

    • Bug Fix: (Though the specification is a little vague.) Changed the operation of transcript files to record all text that appears in the Main Display Panel, and also to record interactions that use the Dialog Panel. Previously, transcript files recorded only text that was output to the Main Display Panel by Wraith Scheme; they did not record any text that the user had entered, and also did not record any interactions involving the dialog panel.

    • Added support for completion of partially-typed names of Wraith Scheme procedures and special forms: To see this feature, press the "escape" key when you have partly typed such a name in the Input Panel.

    • Added a new menu item, "Find in Wraith Scheme Dictionary", to the "Find" submenu of the "Edit" menu, that searches the Wraith Scheme Dictionary for an entry matching the selected text.

    • Bug fix: Fixed a problem that caused a full garbage collection to hang when nearly all of memory was full of non-garbage Scheme objects. (Strictly, the problem was an incorrectly implemented test, that was supposed to report this condition sufficiently in advance to allow the user the options of (1) reducing the size of large data structures and then garbage-collecting manually, or of (2) closing Wraith Scheme in an orderly manner and then relaunching it with a larger memory size.)

    • Cleaned up error-message handling when there is not enough memory even after a full garbage collection: Now, when that happens, all kittens report the problem and all kittens reset to the top-level loop.

    Release 2.12 was the fourth release of Wraith Scheme as a 64-bit application, and the fourth release under the GNU General Public License.

    • Bug Fix: Fixed some incorrect error messages having to do with the Wraith Scheme Foreign-Function Interface.

    • Added some command-key shortcuts to expedite typing into the Input Panel; they are shown in the Interpreter Menu, and are discussed herein in the section on the Interpreter Menu.

    • Added "e::make-offsets", a new utility procedure for Memory-Mapped Blocks, which is useful for dealing systematically with the offsets of variables in the memory-mapped blocks used by the Wraith Scheme Foreign-Function Interface.

    • Added commands to use the Unix "alarm" system from within Wraith Scheme. See the section, Details and Procedures for the Interrupt System.

    • Increased the capacity of the "Load Recent" menu item of the Interpreter Menu from twelve files to twenty-four.

    • Bug Fix: Arranged that when the generational garbage collector is in use, and Wraith Scheme runs out of available file descriptors, Wraith Scheme will do a full garbage collection, which may save the day by reclaiming any file descriptors that are not in use. (The problem was that the generational garbage collector does not reclaim file descriptors, which is necessary to keep it fast. Unfortunately, when I installed the generational garbage collector, I forgot that this particular circumstance needed a full garbage collection.)

    • Changed some details of the Wraith Scheme Interrupt System: Added a mechanism whereby other applications can tell whether a Wraith Scheme interrupt handler has been installed for any given interrupt. Arranged that when the handler for a given interrupt is first installed, changed or removed entirely, the interrupt-requested flag for the corresponding interrupt is cleared. The latter alteration was a bug fix; subtle bugs could occur if an interrupt occurred while the handler was being changed.

    • Added "c::clear-interrupt-requested!", a new procedure to clear the interrupt-requested flag of a Wraith Scheme interrupt. Discussed in the section, SIGUSR1 Interrupts.

    Release 2.11 was the third release of Wraith Scheme as a 64-bit application, and the third release under the GNU General Public License.

    • Bug Fix: Two of the procedures for the Wraith Scheme Foreign-Function Interface had related bugs: Both "e::peek-c-string" and "e::poke-c-string" incorrectly calculated the offset of the desired C string within the referenced memory-mapped block. The errors were complimentary, in that a poke of a string to a particular offset, followed immediately by a peek from the same offset, would appear to succeed; however, the offset to which the string was written and from which it was subsequently read would not be what the user had intended -- the offset would have been zero in all cases. That is, all such reads and writes would have been to and from the start of the memory-mapped block in question.

    Release 2.10 was the second release of Wraith Scheme as a 64-bit application, and the second release under the GNU General Public License.

    • Added a generational garbage collector. See the procedures in the Storage Management section, and the short description in the Garbage Collectors section. By default, Wraith Scheme starts with the generational garbage collector running, but there is a new preference setting to start with it turned off. You cannot turn the generational garbage collector on and off while Wraith Scheme is operating.

      For a somewhat more detailed introduction to generational garbage collection, see the "Glossary" section of the Wraith Scheme Dictionary.

    • Added a mechanism for Wraith Scheme to share large blocks of memory with other processes, via the Unix/C++ "mmap" function, with the intent of facilitating what is in essence a rather coarse-grained foreign-function interface. See the section, Foreign-Function Interface.

    • Added a mechanism for other processes to interrupt Wraith Scheme with other applications, via Unix signal SIGUSR1 and a memory-mappable block of data containing interrupt request registers and Unix process identifiers, with the intent of facilitating what is in essence a rather coarse-grained foreign-function interface. See the section, Foreign-Function Interface.

    • Changed the semantics of the "load" procedure, so that it evaluates all the definitions and expressions that it reads in the interaction environment (which is also the top-level environment).

    • Added "c::load-from-string", which causes the text of its argument to be read in Wraith Scheme's top-level read-eval-print loop.

    • Enhanced "make-string" so that when called with no arguments, it prompts you to type the string content intended in the Wraith Scheme Dialog Panel.

    • Added "e::read-string-with-prompt", which displays its argument -- the prompt -- in the Wraith Scheme Dialog Panel, and accepts a string therefrom.

    • Added procedures to allow a Wraith Scheme process to run as a Unix sockets server. Actually, these procedures have been in Wraith Scheme for a while, but I did not initially document them.

    • Added a simple class system.

    • Added "e::warning", to display a warning message on the default error port and then continue.

    • Bug Fix: There was a bug that did not occur on all Macintosh computers -- in particular, it did not occur on any of mine -- in which the Wraith Scheme Input Panel would scroll intermittently when the user was typing into it. The scroll would move the actual line of text being typed, including the text insertion point, one line down -- out of sight below the bottom of the Input Panel itself. I believe this one is fixed, but it is difficult to be completely certain without having a large variety of Macintosh computers to use for testing.

    • Added "e::block-any-binding", as another procedure useful in critical-section access. See the discussion of Locks and Critical Sections.

    • Arranged that top-level loop variables can no longer be used as the "internal names" of lambda expressions. That was just too confusing.

    • Changed the handling of attempts to divide by zero, from reporting an error to returning a nan or an inf, as appropriate.

    Release 2.00 was the first release of Wraith Scheme as a 64-bit application, and the first release under the GNU General Public License.

    • Made Wraith Scheme a full 64-bit application, with 64-bit pointers and an address space potentially as large as two to the sixty-fourth bytes.

    • Removed support for quarantined short flonums (which were 32-bit floating-point numbers).

    • Changed Wraith Scheme's data type for fixnums from 32-bit to 64-bit.

    • Added an "Internals" document, describing the internal operations of Wraith Scheme, accessible via the Help Menu.

    • Added a way to limit the persistence of objects in Wraith Scheme memory. See the section on Forgettable Objects.

    • Added procedure "e::atom?", to tell whether Wraith Scheme objects are atoms.

    • Added a way to specify, within a world to be loaded, a procedure that will automatically be run immediately after the world is loaded, by binding any procedure of your choice, that required no arguments to be passed to it, to the symbol "e::main", and then saving the world.

    • Released Wraith Scheme 2.00 under the GNU General Public License, which is available while running the program, via menu item "Warranty and Redistribution" of the Wraith Scheme Menu. You may also find the license on line at http://www.gnu.org/licenses.

    • Bug Fix: Logical bit-shift-right of quantities with the most significant bit set (e::bit-shift-right-logical ...), gave the wrong answer. Fixed.

    Release 1.34 was the tenth release of Wraith Scheme.

    • Bug Fix: The procedures "e::show-locks" and "c::release-locks" produced large amounts of spurious output. Fixed.

    • Bug Fix: A saved world could not successfully be loaded when fewer kittens were in use than when it was saved; the first garbage collection after the load encountered a fatal error. Fixed.

    • Bug Fix: Trying to select all of the text in the Wraith Scheme Input Panel did not always work. Fixed.

    • Changed the "Trim Scrollback" command and menu items so that they reduce the amount of scrollable text by half, or to 6000 characters, whichever is greater. The previous value was 20000 characters.

    • Added new procedures "c::disable-window-output", "c::enable-window-output", and "c::window-output-enabled?". See Top-Level Control.

    • Added new procedure "c::kitten-reset". See Keeping Track of Things.

    Release 1.33 was the ninth release of Wraith Scheme.

    • Added as an enhancement, a procedure to tell when main store is ninety percent full. See Storage Management.

    • Bug Fix: Wraith Scheme failed to report an error when attempting to read an unquoted empty list, and unnecessarily reported a fatal error -- instead of a recoverable error -- when attempting to evaluate one.

    • Added as enhancements, procedures c::kitten-input-queue, c::kitten-empty-queue? and c::kitten-purge-queue, for debugging and gathering information about the queues used for interprocess communication between Wraith Scheme kittens. See Interprocess Communication.

    • Achieved modest speed increase -- perhaps a factor of two -- in some circumstances.

    • Achieved modest reduction in consing -- perhaps a factor of two -- in some circumstances.

    • Enhanced the garbage collector to be aware of locality of data use when repopulating Scheme main memory, in an attempt to make Wraith Scheme at least minimally NUMA-aware when NUMA computers become common. (NUMA is short for "Non-Uniform Memory-Access".)

    Release 1.32 was the eighth release of Wraith Scheme. Things new in it included:

    • Bug Fixes: I have done a major rewrite of the low-level locking mechanisms which prevent simultaneous actions by separate Wraith Scheme kittens from corrupting Wraith Scheme's shared main Scheme memory.

      The war story here is, that I had done previous testing of the parallel implementation of Wraith Scheme on a Macbook 13, that only had two processor cores and in fact ran relatively little code in parallel -- it mostly swapped. In late January, 2008, I bought a high-end Mac Pro, with eight cores; it was more than capable of running several Wraith Scheme processes in parallel, and "shook the tree" sufficiently hard thereby, to expose a handful of bugs that had escaped earlier testing.

      They were tough bugs, involving communications and locking between multiple asynchronous processes, and in general never repeated themselves the same way twice. It took three months to get things back to stable. I don't ever want to do that again, but I fear that I shall have to.

    Release 1.31 was the seventh release of Wraith Scheme. Things new in it included:

    • Bug Fix: When Apple Security Update 2007-009 was installed in Macintosh computers with Intel Processors, Wraith Scheme 1.30 lost its ability to perform "paste" and "drag-and-drop" operations in its "MomCat" window. The present release restores that capability. As I released Wraith Scheme 1.30, in late January 2008, it was not clear whether or not Apple would release some form of update that will restore full functionality to Wraith Scheme 1.30; however, Apple had been most concerned and most responsive to my bug report and request for technical assistance with the problem.

    • Bug Fix: An unguarded critical section involving the input queues whereby one parallel Wraith Scheme process can request another to do something, allowed "e::tell-kitten" to fail when the kitten being "told" had only one item in its input queue and was in the process of dequeuing it.

    • Bug Fix: The mechanism to load the last file when updated only worked for the MomCat.

    Release 1.30 was the sixth release of Wraith Scheme. Things new in it included:

    • Bug Fix: Procedure "e::set-current-directory!" returned the string that was its argument, not what it was supposed to, which is #t.

    • Bug Fix: Both set-car! and set-cdr! incorrectly reported an error when their first argument -- the pair being altered -- had a car that was constant. That is, previously:

        (set-car! (list '(1 2 3)) <object>)  ;;  ==> <error message>
        
        (set-cdr! (list '(1 2 3)) <object>)  ;;  ==> <error message>
        

      Both of these error messages were incorrect: Both operations should have worked, and now do.

    • Bug Fix: The procedure "load" did not maintain the value of the current input port.

    • Bug Fix: The compiler incorrectly handled permanent symbols whose values were certain types of Scheme objects. (It worked fine for permanent symbols whose values were procedures, which is the most useful case.)

    • Numerous internal changes: The polite term is "code refactoring". Less polite terms have been reported.

    • Slight enhancements to the efficiency with which the garbage collector compacts non-garbage for reuse.

    • Trivial enhancements for logic programming: The addition of logic-programming capability to Wraith Scheme is a work in progress, and in the release for which this comment is prepared, very little progress has been made.

    • Major enhancements for parallel processing.

    Release 1.21 was the fifth release of Wraith Scheme. Things new in it included:

    • Bug Fix: Procedures displaying and writing strings worked incorrectly when the strings contained percent signs ('%').

        Technical Note: C programmers can probably figure out what caused the problem ...

    • Bug Fix: Wraith Scheme allows numeric constants to be written as fractions -- such as 22/7 -- but has no means to store numerator and denominator separately; instead, it performs the division and returns the result. Previously, the result returned was reported as inexact, even when the division could be carried out with no loss of precision. I have been able to refine that report, but only on Macintosh computers, such as the Macbook series, that use Intel processors.

      On Macintosh computers that use Intel processors, Wraith Scheme now returns an exact result when the result of the division is exact. For example:

        1/2  ;;  ==> #e0.5  ;;  Exact.
        

      but of course

        1/3  ;;  ==> 0.33333333333333331  ;;  Inexact.
        

      On Macintosh computers that use PowerPC processors, Wraith Scheme still returns an inexact result even if the result of the division is exact. For example:

        1/2  ;;  ==> 0.5  ;;  Inexact.
        

      and of course

        1/3  ;;  ==> 0.33333333333333331  ;;  Inexact.
        

      The philosophy here is that Wraith Scheme will report numbers as inexact unless it can prove that they are exact.

        Technical Note: For some reason, I cannot get the floating-point exception mechanism to work on the old G3 Mac that I use to make sure Wraith Scheme works on both Intel and PowerPC architectures. I do not have access to a G4 or G5 Mac for further investigation.

    • Bug Fix: Fixed a problem with the hygienic macro implementation that was causing failure of "do" statements in which not all declared local variables were automatically recalculated each time through the loop. That is, "do" statements such as

        (do ((i 0))
            ((= i 3) #t)
            (display i)
            (newline)
            (set! i (+ i 1)))
        

      did not work, whereas the seemingly-equivalent

        (do ((i 0 (+ i 1)))
            ((= i 3) #t)
            (display i)
            (newline))
        

      worked just fine. Now, both work.

    • Added as enhancements, several procedures to aid in formatting numbers.

    • Added as an enhancement, a procedure to test or demonstrate Wraith Scheme's fatal-error reporting mechanism. See the last entry in the section on Top-Level Control.

    • Added as enhancements, procedures to examine and modify the internal flags used to determine whether Wraith Scheme compiles defines automatically and whether Wraith Scheme displays numbers with the full precision available. See the section on State Flags.

    • Added many images to the Wraith Scheme Help File. The substantial increase in application size that occurred with this release is almost entirely due to these images.

    • Added a menu item to the Font Menu to allow choice of the text color that Wraith Scheme uses.

    • Added a "Preferences" item to specify text color.

    Release 1.20 was the fourth release of Wraith Scheme. Things new in it included:

    • Wraith Scheme became an "R5" Scheme implementation.

    • Modest compiler enhancements.

    • Added an Interpreter Menu menu item to disable output to the main window; such output is slow, so this feature may allow your programs to run faster.

    Release 1.11 was the third release of Wraith Scheme. Things new in it included:

    • Bug Fix: The hygienic macros implementation had some problems, most of which evidently stemmed from me misreading the specification.

    • Modest compiler enhancements.

    • All the files that used to be provided separately, as supplements to the distribution, were contained within the application itself, accessible via the Help Menu.

    Release 1.10 was the second release of Wraith Scheme. Things new in it included:

    • Bug Fix: Fixed a significant bug that affected both cut-and-paste into the Input Panel and drag-and-drop into Input Panel: Performing either of these operations with more than one line of text at a time caused Wraith Scheme to hang; that is, the multicolored spinning cursor appeared and the program accepted no further input.

    • Bug Fix: The "Trim Scrollback" command did not work quite as documented -- it was always trimming the scrollback so that at most 20000 characters remained, instead of cutting it by half to a minimum of 20000 characters. Fixed.

    • Bug Fix: The "Load Last File When Updated" feature did not deal correctly with an unsuccessful load; that is, one that produced an error. In that such cases, it would keep trying to reload the same file over and over again, getting the same error message each time. Fixed.

    • Bug Fix: There used to be some glitchiness in scrolling of the Wraith Scheme Main Display Panel; when you typed blank lines in the Input Panel, the main display panel would alternate between scrolling two lines and not scrolling at all.

    • Increased the largest available font size for the Wraith Scheme Window to 36-point.

    • Additional help file and Help Menu command: The new file is a dictionary of Wraith Scheme commands and terminology.

    • Procedure "e::error-port", to retrieve the port where Wraith Scheme prints error messages.

    • "Load Recent..." menu item, to allow easy reloading of recently-loaded files.

    • "Load Next-to-Last File" menu item, to reload the next-to-the-last file loaded.

    • "Change Background Color..." menu item, to change the background color of Wraith Scheme text areas.

    • "Preferences" item to specify background color for Wraith Scheme text areas.

    • Initialization with large amounts of memory is vastly faster.

    • Garbage collection with large amounts of memory is notably faster.

    Release 1.00 was the first release of Wraith Scheme; it was all new.


    What Might be New in the Future:

    Besides fixing bugs and making internal improvements, my wish list for the future of Wraith Scheme includes the following items, which are not necessarily in the order in which I might get around to doing them:

    • A "bignum" package.

      The GNU folks have a library for multiple-precision arithmetic which might allow a relatively straightforward implementation of bignums in Wraith Scheme, and I am tempted to see if I can create one.

    • More support for logic programming.

    The future probably does not hold:

    • "R6" or "R7" compliance.

      R6 and R7 are much more complicated Scheme standards than R5. I find most of their new features neither interesting nor useful to me personally, and there are other aspects of Scheme that I would prefer to work on. In that context, I simply don't have time enough to develop and maintain an R6 or R7 implementation of the quality that I would like to present as shareware.

      There has been a great deal of controversy about these standards in the Scheme community. I hope no one will misconstrue lack of support for them in Wraith Scheme for me taking a position against the new standards and being too cowardly to say so. The plain truth is that I find the new standards overwhelming: They are too complicated for me to support all by myself.

      Notwithstanding, I may implement some features of versions of Scheme later than R5, if I find them useful and if they do not conflict too much with existing features of Wraith Scheme.


    Miscellaneous Information:

    Numbers Revisited:

    Many people think the different kinds of numbers are separate, so that no number can be of more than one kind. They think that integers and reals are different kinds of numbers, so that no integer can be real, and so on.

    That's false. They overlap. Specifically, every integer is also rational, real and complex; every rational is real and complex; every real is complex; and they're all numbers.

    For example, 1 is an integer. It is also a rational (being equal to the quotient of two integers, namely 1/1, or 2/2, or ...), it is on the real line, and it is also in the complex plane -- we could write it as 1 + 0i. Typographic choices, such as the inclusion of a decimal point, an exponent, or trailing zeros, do not alter the mathematics. Thus "1", "1.", "1.0", "1.0e0" and "1.000000000000000" are all ways to write the same mathematical number; namely, the integer "one".

    Some wag once said, "A computer scientist is a person who refuses to believe that 1.000 is an integer." Don't you make that mistake.

    Much of this confusion is because many computer languages use different kinds of machine storage for numbers given with a decimal point than for numbers given without one. A language might store "1.0" as an IEEE 64-bit floating-point number, yet store "1" as a 32-bit word with just the least-significant bit turned on. It is the integer "one" in either case. Some people think floating-point numbers cannot be integers. That's wrong. What counts is the value, not how it is stored.

    If you want to know how Wraith Scheme is storing a particular number, there are some special predicates you can use to find out.

    More confusion stems from the fact that typographic conventions are widely used to convey information about the precision of a number when that number is imperfectly known. Thus in science or engineering, use of the string "1.000" to represent a number often means "the number I am talking about is between 0.9995 and 1.0005, but I don't know for sure what it is, so I will use the representative exact value 1 to stand in its place, and will give the value to four significant digits to hint at the level of my ignorance." This convention obscures the mathematics of numeric types: Thus in the example given, the integer 1 might actually be standing for the integer 1, for the rational number 10001/10000, for the irrational number (1 + pi/10000), or for the complex number (1 + i * pi/10000).

    The built-in features of Scheme are in any case not powerful enough to support such conventions: You can specify that a number is exact or inexact by means of the "#e" and "#i" prefixes, but Scheme records only that one bit of information. There is no built-in means to tell Scheme how many digits of precision the number has. As far as input to Scheme is concerned, the strings "1.50000" and "1.5" indicate the same number and the same status of the "exact bit". (In the case of Wraith Scheme, that number is the inexact rational 1.5.)

    Just for fun, ask yourself (A) Which of the following numbers are complex? (B) Which are real? (C) Rational? (D) Integer?

      42
      1.
      -2.000000
      345.62e2
      9.6149604775e23
      

      (Hint: Does 9.6149604775e23 have a fractional part?)

    Answers: (A) All of them. (B) All of them. (C) All of them. (D) All of them.


    Technical Details About Wraith Scheme:

    Where do I start ...

    • Wraith Scheme is a descendant of Pixie Scheme, which I wrote for mid-to-late-1980s versions of the Macintosh. Much of Wraith Scheme's technical minutiae stem directly from Pixie Scheme, but for clarity (hah!) I will not always distinguish between Wraith Scheme and Pixie Scheme in this section.

    • Pixie Scheme was written in C, specifically in Macintosh Programmer's Workshop (MPW) C versions up through 2.0.2. I developed it on a plain vanilla Macintosh II with 1 MByte of memory -- no typo, that's "M" for "mega". It ran fine in one megabyte. (It takes more now -- I suggest 10 MByte.) I later bought some more memory, and ported Pixie Scheme to later versions of MPW C -- the last was 3.2b3.

    • Many failings or biases in the first release of the program no doubt reflected the specific nature of my environment. For example, 1 MByte wasn't enough memory to test Pixie Scheme adequately under MultiFinder. For example, Apple didn't get around to releasing an MPW C compiler that generated correct 32-bit code until after I had shelved the Pixie Scheme project. (That last was very frustrating. My early Mac II was not a full 32-bit machine in any case; it had an Apple memory-management unit that handled only 24-bit addresses, so the program worked fine for me. Yet I would get bug reports from folks using 32-bit machines that I couldn't so much as duplicate, much less fix. I didn't even know anyone who owned a 32-bit Macintosh at the time.)

    • More recently, I ported Pixie Scheme to run in a Unix (Terminal Application) window in macOS X on a 2006 Macbook, using the GNU C compiler. That was enough of a change to warrant changing the name of the program, to Wraith Scheme. First I made it run using only the simplest kind of terminal input and output -- the kind of thing that would work on a teletype. Then I got a version going that used the Unix "ncurses" package for terminal output.

      The experience of excising the essence of Pixie Scheme from an old-style Macintosh application, and getting it going under Unix with two quite different styles of I/O, gave me a chance carefully to encapsulate the code for a functioning Scheme interpreter separately from the code that let that interpreter communicate with the outside world. I had actually planned it that way -- even when I was writing Pixie Scheme, I anticipated that maybe, some day, it would run in a different I/O environment, and therefore I tried to perform the encapsulation at that time. I was setting up for what is now called the "model-view-controller" pattern of software design and implementation, though I am not sure that the term existed then.

      From there, it was straightforward -- I won't say easy -- to wrap a more modern Macintosh-style user interface around the Scheme interpreter. It took about a month of serious effort to get Wraith Scheme going as a Macintosh application. The Macintosh wrapper was a "Cocoa" application that involved not quite three thousand lines of Objective C (more now) and a set of interface classes (".nib" file stuff) built in Apple's Xcode development environment.

    • After a while, I thought of a way to make Wraith Scheme a parallel Scheme, with many separate Wraith Scheme processes (Unix processes) sharing a common Scheme main memory. It took a fair amount of work to make that work.

    • Still more recently, I decided to make Wraith Scheme a full 64-bit application, and to use 64-bit pointers in Scheme main memory as well. I also decided to make subsequent versions of Wraith Scheme require macOS 10.6 ("Snow Leopard") or better, which in turn meant that they required a Macintosh with an Intel processor. (The last version before that change, that will run on macOS 10.4 ("Tiger") or better, and that will run on a Macintosh with either an Intel processor or a Power PC processor, is still available; it is Wraith Scheme 1.36. I expect I will support it in the sense of fixing any major bugs that show up.) (Incidentally, I knew that it would in principle be possible to make a 64-bit version of Wraith Scheme that would run on a Macintosh that had a "G5" version of the PowerPC; I did not attempt to create one simply because I did not have a G5 Mac to test it on.)

      I changed to 64 bits and to Snow Leopard or better to allow Wraith Scheme to have a larger Scheme memory, and to allow me to use some of the special Macintosh software features that were not available in earlier versions of macOS.

    • Wraith Scheme 2.26 both raised and lowered the bar for the system requirements for running Wraith Scheme. That version raised the bar by requiring macOS 11.6 (Big Sur) or later, but it lowered the bar by being able to run on both Macintosh computers using Intel processors and Macintosh computers using Apple proprietary processors ("Apple silicon").

    • Wraith Scheme is a tagged-object Lisp. Each object comprises a 64-bit tag and a 64-bit entity that is either an immediate datum or a pointer. The precise nature of the object is indicated by the tag, which has seven bits of type information, two bits for cdr-coding, two for the garbage collector, and lots more for miscellaneous internal purposes. I do not document the tag bits because the tagging scheme is likely to change in the future.

    • Pixie Scheme began life as an SECD machine, but it and its descendents soon changed. The present Wraith Scheme implementation stores information equivalent to the contents of the D register on the stack, and uses a stack and a continuation which are separate data structures, not heap objects. It makes copies of the entire stack and of the entire continuation, in the heap, when necessary; for instance, when "call-with-current-continuation" is called.

    • The virtual machine that describes the implementation includes about a dozen registers. There is a stack pointer, which refers to a stack that is implemented as a data structure separate from the heap. There is a pointer to a list of environments. That entire list is a heap object. A continuation register points to a separate data structure that represents a list of instructions next to be executed; it is essentially the C register of an SECD machine. There is a register for returning tagged-pointer values from functions, a register for the top-level environment, several registers for ports in use, and a couple of scratch or special-purpose registers.

    • I have touched on the structure of Wraith Scheme's environment list while discussing the debugger, and will not repeat myself here. What gets put on the stack is too complicated and too subject to change to discuss.

    • Almost all text Wraith Scheme prints out, including most error messages and function names, is stored internally in large arrays of text strings. They used to be Macintosh resources when I was building Pixie Scheme with the old Mac ToolBox. Thus it should be relatively easy for an experienced C programmer, who has access to the source files for the Wraith Scheme program, to create custom versions of Wraith Scheme, or to port the program to another (human) language. Since the only language I speak well is English, I have not tried to do so myself, so I have undoubtedly forgotten to make it easy to alter some things that would need to be changed, but I suspect that what I have done would be helpful in such a project. If anyone is interested in such a port, contact me.

    • Wraith Scheme is written in a strongly object-oriented fashion. (If there had been a decent C++ or similar object-oriented variant of a C compiler available for the Macintosh at the time I started writing Pixie Scheme, I certainly would have used it.) There is lots of data encapsulation. There are many objects (structs) which contain (pointers to) functions which perform various operations, whose details differ from object to object. Thus I have a generic method for printing objects, which contains no "switch" statement: It merely looks up the "print" method for the object itself. In consequence, for a C program, Wraith Scheme is remarkably easy to modify and maintain. For example, it took only a few hours to add support for IEEE 80-bit floating-point numbers to Pixie Scheme, and most of that time was spent writing functions that actually implement operations on these entities, not in tying 80-bit floats into the control-flow structure of the program.

    • The stand-alone Macintosh version of Wraith Scheme is threaded: One thread runs the C code that does the work of the Scheme interpreter; it is the "model", in the model-view-controller design pattern. The controller and view use at least two more threads. (I am never quite sure how many threads the operating system has added to the application, and that number seems to change from time to time.)

      The view was originally an interface built using Apple's Interface Builder application, together with code for a couple of supporting controllers for specific parts of the view. Now I build it with the features of Apple's Xcode application, that approximate the old Interface Builder.

      The main controller -- the one that sits between the model and the view -- has two parts. The first part comprises a fair number of semi-autonomous methods whose actions are triggered by various events, such as typing "return" in the Input Panel or operating one of Wraith Scheme's buttons or menu items. The second part is a timing mechanism, that polls the evaluator thread regularly, to see if it has anything for the controller to do.

      The controller and model communicate via two one-way critical sections -- one for data flow in each direction -- with critical-section access controlled by pthread locking mechanisms. (And if critical-section access control were all there were to getting communicating entities to cooperate, my life would be much simpler.)


    Scheme References:

    These items are all from my bookshelves or download directories. Many have more recent editions.

    • Harold Abelson, Gerald Jay Sussman and Julie Sussman, 1985. Structure and Interpretation of Computer Programs, MIT. This is an introductory book on computer science for very bright college students. It both teaches and uses an earlier version of Scheme than that described in the R5 report, but the differences are minor. It features many excellent examples of programming in Lisp in general and in Scheme in particular. The early sections are a good introduction to the language, the later ones show well just how powerful it is.

    • William Clinger, "The Semantics of Scheme", in Byte 13, no. 2, 221-227 (February 1988). One of several introductory articles on Lisp-class languages in this issue of Byte.

    • William Clinger and Jonathan A. Rees (editors), 1991. "The revised 4 report on the algorithmic language Scheme", Lisp Pointers 4(3), ACM. The RN reports (N = 3, 4, 5 ...) define Scheme. They are manuals, not tutorials -- readable once you have some background in any Lisp, and invaluable for specific details, but likely to intimidate a beginner. I strongly recommend that you get copies anyway.

    • R. Kent Dybvig, 1987, and subsequent editions. The Scheme Programming Language, Prentice-Hall. The third edition is MIT Press, 2003. An outstanding work describing Scheme. Later editions describe the "R6" variety of Scheme; thus they are perhaps less directly appropriate for learning Wraith Scheme, which is an "R5" Scheme.

    • Daniel P. Friedman, William E. Byrd and Oleg Kiselyov, 2005. The Reasoned Schemer, MIT. Scheme integrated with logic programming.

    • Daniel P. Friedman and Matthias Felleisen, 1974, 1986, 1987, and subsequent editions. The Little Lisper, MIT. An introductory book on programming in a style appropriate to Lisp, as a programmed text. The more recent editions use a dialect of Scheme as the language in which the examples and discussion are presented.

    • ____, 1987. The Little Schemer, MIT. An introductory book on programming in Scheme, as a programmed text.

    • ____, 1987. The Seasoned Schemer, MIT. A more advanced book on programming in Scheme, as a programmed text.

    • Richard Kelsey, William Clinger and Jonathan Rees (editors), 20 February 1998. "Revised5 Report on the Algorithmic Language Scheme". Available on several Internet sites, such as http://www.schemers.org.

    • Jonathan Rees, "The Scheme of Things: The June Meeting", Lisp Pointers V(4) (October-December 1992).

    • J. Rees and W. Clinger (editors), "Revised3 Report on the Algorithmic Language Scheme", ACM SIGPLAN Notices 21, no. 12, 37-79 (December 1986); also an MIT Technical Report.

    • George Springer and Daniel P. Friedman, 1989. Scheme and the Art of Programming, McGraw-Hill. A good introduction to programming and to Scheme. More elementary than Ableson, Sussman and Sussman.


    Lisp References:

    These items are all from my bookshelves. Many have more recent editions.

    • Harold Abelson and Gerald Jay Sussman, "Lisp: A Language for Stratified Design", in Byte (magazine) 13, no. 2, 207-218 (February 1988). One of several introductory articles on Lisp-class languages in this issue of Byte.

    • John Allen, 1978. Anatomy of Lisp, McGraw-Hill. An oldie but a goodie. This rather advanced text describes Lisp mechanisms and data structures in considerable detail, with attention both to theory and to practice. Not for the faint of heart.

    • Eugene Charniak, Christopher K. Riesbeck and Drew V. McDermott, 1980. Artificial Intelligence Programming, Lawrence Erlbaum Associates, Publishers. An oldie but a goodie. The first section is titled "Advanced Lisp Programming", but after that it gets harder.

    • Peter Henderson, 1980. Functional Programming Application and Implementation, Prentice-Hall. An outstanding book on the theory, design and implementation of a class of Lisp-like functional programming languages. Wraith Scheme was originally based on the implementation of an SECD machine described herein. I found the book to be somewhat a "sleeper": I almost stopped reading it because the material in the first few chapters seemed elementary and familiar. I am glad I continued.

    • Samuel N. Kamin, 1990. Programming Languages: An Interpreter-Based Approach, Addison-Wesley. How to implement any language you like as long as it's Lisp. Well, not really: This book gives miniature, subset implementations of a representative selection of modern programming languages, all of whose syntaxes have been modified to be Lisp-like (prefix notation with lots of parentheses). The common syntax highlights the differences in semantics. The languages covered include a rather standard simple Lisp as well as simple subset of Scheme.

    • Guy L. Steele Jr., 1984. Common Lisp, Digital Press. The definitive guide to that dialect of Lisp which has the greatest claim to being a standard. As a manual, the book is excellent, but it is neither a tutorial nor an introduction. There is a revision (1990), even thicker than the original, that goes into more detail on such special features as the Common Lisp Object-Oriented Programming System.

    • David S. Touretzky, "How Lisp Has Changed", in Byte (magazine) 13, no 2, 229-234 (February 1988). One of several introductory articles on Lisp-class languages in this issue of Byte.

    • Patrick Henry Winston and Berthold Klaus Paul Horn, 1981, and subsequent editions. Lisp, Addison-Wesley. This rather venerable tome is still a good introduction to Lisp and to what you can do with the language. The first edition uses the "MACLisp" dialect, which is no longer widely used (unless you have in your garage a DEC-20 mainframe or a Symbolics 36XX with down-rev software), but I believe that subsequent editions use Common Lisp. I learned Lisp with this book and a DEC-20. No, not in my garage. (Though I do have a friend who used to have a DEC-20 in her garage.)


    Other References:

    • Daniel P. Friedman and Carl Eastlund, 2015. The Little Prover, MIT. A book describing the implementation of a theorem-prover, written in a simple dialect of Lisp, as a programmed text.

    • Douglas R. Hofstadter, 1979. Goedel, Escher, Bach, Basic Books. A thoughtful and deep book that deals (among other things) with some interesting and profound concepts that are part of computer science, and with other interesting and profound concepts that would be part of computer science if anyone could figure out how to work them in.

    • Richard Jones and Rafael Lins, 1996. Garbage Collection: Algorithms for Automatic Dynamic Memory Management, Wiley. An oldie but a goodie.

    • Tracy Kidder, 1981. The Soul of a New Machine, Little, Brown and Company. Everyone contemplating a career in the computer industry should read this book, the better to recognize job offers from places like this. What you do when you do get such an offer is up to you: I recommend you accept. But remember, misery loves company.

    • Jim Ledin, 2022. Modern Computer Architecture and Organization, second edition, Pact. A good summary of the field, including recent developments.

    • Pamela McCorduck, 2004. Machines Who Think (25th-anniversary update), A. K. Peters. Well-informed and thoroughly readable history of artificial intelligence.

    • David A. Mindell, 2008. Digital Apollo: Human and Machine in Spaceflight, MIT. Long ago, in a galaxy far, far away, the first digital computer used in an aerospace vehicle that carried a crew, flew on board the first spacecraft to visit another world. The whole Apollo flight computer was rather less capable than most of today's five-dollar microcontrollers, but the interface design for it was challenging, and figuring out what it would do and how the crew would interact with it was more so. It was also robust: Could your laptop reboot without a blink in a fraction of a second if your house took a lightning strike??

    • Stuart Russell and Peter Norvig, 2021. Artificial Intelligence: A Modern Approach, fourth edition, Pearson. A comprehensive recent textbook on the subject, or at least a thick one.

    • Dr. Seuss, 1955. On Beyond Zebra, Random House. As this classic children's book so clearly demonstrates, a language should allow you to express not only things you can think of, but also things you could not possibly have thought of if you hadn't learned it.

    • P. W. Singer, 2009. Wired For War: The Robotics Revolution and Conflict in the Twenty-first Century, Penguin. A thought-provoking read about some of the scarier aspects of artificial intelligence. Military robots are widespread, effective, sometimes already have autonomous authority to shoot, and are in general specifically designed to disobey Asimov's three laws of robotics.

    • Guy Steele, 1983. The Hacker's Dictionary, Harper and Row. A funny and quite accurate guide to the jargon of the best of the best of computer scientists. This dialect is to programmers what Chuck Yeager's drawl is to test pilots. I thought the book was a joke until I joined an artificial intelligence lab and found everybody really did talk that way. In this context, incidentally, "hacker" has a much broader and more respectable meaning than its popular use as a synonym for "computer criminal". Virus weenies and digital peeping Toms do not deserve so honorable a label.

    • Edward R. Tufte, 1983. The Visual Display of Quantitative Information, Graphics Press.
    • Edward R. Tufte, 1990. Envisioning Information, Graphics Press.
      These books are the main reason I laugh uproariously whenever anyone tells me that some computer or program has decent graphics or a decent user interface. Few books on computer graphics or computer interfaces mention any of Tufte's works; perhaps the authors are too embarrassed.

    • David Michael Ungar, 1987. The Design and Evaluation of a High Performance Smalltalk System, MIT. If you read the table of contents and the back cover casually, you might think that this book is about implementing Smalltalk on a chip. Actually, the lion's share of the text deals with good and better garbage collection algorithms.


    Whimsy:

    On Dialectic and History:

    Letter sent in 1988 to the editors of "The California Tech", California Institute of Technology, Pasadena, California:

      Dear Editors:

              This June it will be twenty years since I graduated from CalTech. The anniversary has made me think: Possibly my thoughts may be relevant to the present student body.

              After CalTech I went to graduate school at the University of California's Berkeley campus. It was like going from a monastery to a madhouse. From the late sixties through the mid seventies, Berkeley was a tumultuous place, full of people who were bound and determined to change the world. What's more, each of them seemed to have a different idea of how to go about it. I could scarcely pass through the campus gates without being accosted by handbill-distributors from every imaginable kind of political, social and economic action group, all bent on modifying civilization to suit themselves. There were libertarians and communists, republicans and democrats, churches both bizarre and familiar, ecological advocates, draft resisters and military recruiters. The campus seemed overflowing with charismatic, impressive leaders, each with a separate agenda for social change.

              Often there were demonstrations: I rapidly learned to cut through the back alleys to get to class. Sometimes there was real violence: Once I walked out of the student cafeteria after lunch, only to find a National Guard helicopter spraying the plaza with riot-control gas. I decided I had better go back inside and have dessert.

              I was too bewildered to have anything to do with all this. I gravitated toward a small, ever-changing circle of science buffs and technology enthusiasts, people much like myself. We had no charisma and impressed no one. We would go out for pizza and talk. The discussion topics changed as our informal membership varied -- many liked space exploration, one was enthusiastic about computers, and so forth. If pressed, most of us would cautiously admit to some faith that scientific and technical developments were also important agents of social change, but in the midst of widespread turmoil of a different kind, I found such a belief ever less tenable. As the years passed, it seemed increasingly that the things I was interested in could scarcely matter in comparison to those other, more powerful forces.

              Yet in retrospect, it is remarkable how little came from the social and political movements of that time and place. I no longer remember the names of any of the Berkeley people who were out to change the world. I scarcely recall what they wanted to do. After a decade or two I must look hard to find our planet any different for their efforts. It seems that in the long run, they mattered all but naught.

              To my embarrassment, I have even forgotten most of the people in our group of pizza-eaters. Only recently did someone remind me of the name of the fellow who came to a few meetings and talked with such zeal about the prospect of small computers and their likely capabilities.

              His name was Steve Wozniak.

              Keep the faith.

                                                                      Jay Reynolds Freeman

                                                                      BS (Physics) '68
                                                                      May 1, 1988


    Excuses:

    The following excuses may help explain flaws or omissions in Wraith Scheme:

    • I do not possess your level of wisdom, insight or experience.

    • My computers have included Mac Pros, a Mac Mini, several kinds of Macbooks, and a G3 iBook. I did not test Wraith Scheme extensively on other kinds of Macintosh.

    • I didn't think of that.

    • I was interested in writing a Lisp system, not a (choose one or more)

      • text editor.

      • ToolBox interface.

      • NS object interface.

      • whizzy graphics package.

      • ...

    • I forgot.
    • I did not test Wraith Scheme under older versions of Apple's system software.

    • I didn't know how.

    • I thought I knew how, but I was wrong.

    • I was too lazy to do it right.

    • I did it the the way the R5 report says it should be done.

    • I did it my way.


    Infrequently Asked Questions:

    • "Why would a sane person want to write a Lisp system?"

      < long, long pause ... >

    • "Well, why did you want to write a Lisp system?"

      To begin with, I know of no programming language better than Lisp for building large programs rapidly: Lisp has been described as the best language in the world for building castles in the air, single-handed, overnight. Furthermore, I am interested in various technical details of implementing Lisp, in possible future Lisps and Lisp-like languages, and in blazing fast compilers.

      Given my fondness for Lisp programming, obviously I wanted a Lisp system around. Given my interest in internal details, developments and modifications, obviously I needed a system with source code, and I needed to be thoroughly familiar with that code. There were (and still are) several Lisps available with source, but the best means I know to become familiar with all the details of a large piece of code is to write it.

    • "Why Scheme?"

      Scheme is a small modern Lisp, well in the forefront of research in Lisp-like languages, whose nature allows it easily to be compiled.

    • "Why not Common Lisp?"

      Common Lisp is much larger than Scheme, in the sense of lines of code and time required to write them. Furthermore, in my opinion, Common Lisp is not nearly as elegant as Scheme.

    • "Why the name 'Wraith Scheme'?"

      I had cat named "Wraith" whom I dearly loved.

    • "Why name a program after a cat?"

      It makes as much sense as naming a computer after a raincoat. (Mee-yow!!)

      (For the record, evidently Jeff Raskin deliberately misspelled "McIntosh" -- which is a variety of apple -- when he named Apple's new "computer for the rest of us". But so what -- it's still named after a raincoat!)

    • "How do you pronounce 'Wraith'?"

      It rhymes with "faith".

    • "Why are you so fond of cats, anyway?"

      To those who understand, no explanation is necessary; to those who do not understand, no explanation is possible. Besides, they remind me that love can be unconditional, that innocence is worth protecting, and that curious optimism may discover a solution when greater minds would give up in despair.

    • "What's with all the weird humor and obscure references in this document, and with the occasional whimsical behavior of Wraith Scheme?"

      If you can't make it insanely great, at least make it greatly insane. Besides, where else would I find a captive audience for my jokes?

    • "What can we expect in future versions?"

      Who said anything about future versions? Oh, excuse me, I did. Well, there's an old Russian proverb: "Hope for the best, and expect the worst; thus you shall never be disappointed." What you should expect is very little, perhaps not even bug fixes if I get busy or distracted. One might hope for a faster and better compiler, more speed in general, a more powerful interface to the file system, a better debugger, and access to a small selection of whizzy Macintosh special features. I doubt there will be a built-in editor, a graphics package, or anything like a complete interface to all the NS functions in known space.

    • "How am I supposed to write, edit and enter Scheme source code?"

      Use a text editor, and load the file. I use EMacs, but many others would do as well. On a Macintosh, cut-and-past and drag-and-drop work very well for moving small bits of text into or out of the Wraith Scheme window, as do the various buttons and menu items for loading recent files and for loading files when updated.

      Note that the "Automatically Reload Last File After You Save Changes" menu item of the Interpreter Menu, and the button of the same name in the Basic Buttons drawer, provide a way to load a file whenever it is updated. Thus you may use whatever editor you like to work on a file, and have Wraith Scheme load it for you automatically, as soon as you save your changes.

    • "Why did you wait so long to release source for Wraith Scheme?"

      Portions of the source code embarrass me: How many cooks -- even good cooks -- want people to see what their kitchen looks like? (For me, cooking is not so much an art as a survival skill -- how to stay alive in the kitchen.) Besides, I did not wish to deprive you of the chance to write your own 80000-line program ...

    • "Why didn't you release source code for the 32-bit version of Wraith Scheme, too?"

      It was a clear case of laziness versus masochism. Laziness won.

    • "Do you think Wraith Scheme will ever turn into a commercial product?"

      For any chance of commercial success, I would have to do a compiler with a decent native-code code generator. Otherwise, the system's speed will not be competitive. I'm not sure I want to bother; I am interested in other aspects of compilation.

      Besides, I do not sense that the world is waiting with bated breath for yet another artificial-intelligence programming-language implementation just now. (Too bad -- I know a lot of people who could use some more intelligence, artificial or otherwise, perhaps most notably me ...)

    • "Could you tell us a little about the development history of Wraith Scheme?"

      Wraith Scheme is a direct descendant of "Pixie Scheme", which I developed in the late 1980s and early 1990s. Pixie was another cat. Pixie Scheme ran on Apple Macintosh computers of that vintage, was available as shareware -- I asked for a shareware donation of one dollar -- and was actually used here and there. I shelved the Pixie Scheme project in 1991, because it had grown to the point where it was pushing on the limits of my original-model Mac II and of my development software. In 2006, I finally got a new Macintosh, and decided to do a port of Pixie Scheme to run in a Unix terminal shell on my Macbook under macOS X. First I got it to run using the GNU C compiler, then subsequently the GNU C++ compiler, then I wrapped a contemporary Macintosh interface around it. Those changes seemed enough to warrant a new name; hence "Wraith Scheme".

      My "Wraith Scheme" files include over 80 000 lines of source code -- C++, Objective C and Scheme -- and more than a million lines of test programs and expected answers to those tests. It took the equivalent of several years of my professional time to create Wraith Scheme, in isolated intense bursts spread over the five years 1987-1991, and in the period from late 2006 to date. I am a moderately experienced C and Lisp programmer (I have probably written over 300 000 lines of finished code of the former, including C++, and 75 000 lines of the latter), but Pixie/Wraith Scheme is by far my most complicated program ever.

      Pixie Scheme was my first stand-alone Macintosh application. (I must have learned something: It took less than twenty minutes to write the second one.) As Macintosh applications go, Pixie/Wraith Scheme is a little weird: For example, it sort of does not have a main event loop. (Pixie Scheme really didn't have one; Wraith Scheme uses one for the user interface, but not for the Scheme interpreter itself.)

      The greatest single break I had in this project was that I did not have access to a personal computer when I started working on Pixie Scheme: I spent over six months in design and analysis without writing a single line of code. I am certain that without so spending this time, I would not now be nearly as far along with Wraith Scheme as I am, and I strongly suspect that I would have given up on Pixie Scheme in the first place.

      In developing Pixie Scheme, I missed a utility like Unix's "lint". I missed a C compiler that accepted function prototypes. I missed a lot of sleep. I missed a source-level debugger. I used MacsBug now and then. The combination of Apple's Xcode development environment and GNU tools that I use for Wraith Scheme, is a whole lot better than what Apple provided in the late 1980s.

      I have one good war story: I gave the booleans #t and #f each an identifying tag, so it didn't matter what the datum part of one was. When I wanted a Pixie Scheme routine to return #t or #f, I would just set the tag of the return value appropriately, and ignore the rest. But then I made a mistake in the routines that compared them for equivalence; they tested not only the tag but also the 32-bit datum with which it was associated. I had inadvertently created 2 to the 32 kinds of #t, all printing alike but no two equal. And I had done the same for #f. I was reminded of Arthur C. Clarke's short story, "The Nine Billion Names of God". And I was pleased to have modeled reality well, by creating almost enough kinds of truth for every human alive to have a different version. That might be sufficient even for politicians, religious authorities, and business executives.

      The on-line documentation for Wraith Scheme comprises approximately 200 000 words. War and Peace runs about 460 000 words in English translation, so I still have a way to go. Pride and Prejudice has only about 124 000 words, so there may be hope. (And my cats have included Darcy, Willoughby, Emma, Elinor and Knightley.)

    • "Why didn't you use (Object Pascal / Forth / C++ / Ada / Cobol / Java / Microsoft Basic / <whatever> )"

      Er, ah, um... because! That's why -- because! Seriously, I bought my Macintosh II because it was almost surely the most powerful personal computer system then available (in the second quarter of 1987), particularly for programs that required easy access to much memory. I knew C well, and there were good C development systems available for the Mac II. The only decent object-oriented language available for the Mac at that time was Apple's Object Pascal, but I have never particularly liked Pascal. So I developed Pixie Scheme as a C program, using the Apple "Macintosh Programmer's Workshop" environment and compilers. There were no decent C++ compilers for the Macintosh when I started the Pixie Scheme project, so C++ was not an option at the time. (The GNU folks were working on what was to become g++, but it was still pretty buggy and no one had yet ported it to the Macintosh. The C compiler that I started out with was not even ANSI-standard C.)

      Java provides no access to the low-level Unix utilities that I have used to make Wraith Scheme a parallel implementation and to provide its foreign-function interface. Furthermore, I find Java poorly suited to object-oriented programming, and I am quite certain that without a strongly object-oriented approach to design and implementation, my Scheme implementation would have died stillborn.

      Cobol would have been, ah, fascinating.

    • "I thought a main event loop was part of the One True Way to write Macintosh programs."

      Well, neener, neener, neener. And what's more, there isn't any Santa Claus.

    • "Why don't you ask for some particular amount in your request for a shareware donation?"

      I think it should be you who decides what my software is worth.

      I might not even ask for shareware donations at all, except that I am curious who uses Wraith Scheme, and where, and for what. I can get some idea of the first two by tracking shareware donations. Basically, I provide Wraith Scheme as a public service: I won't go on about how I think people ought to make some such use of their time and energy, but I do think so. I work professionally in computer science and am presently doing okay (knock on wood). Putting a useful piece of software into the field is one way of saying "thank you" to a lot of people who have made things easier for me, and is perhaps a way to acknowledge and encourage many others who have missed various boats that I have been lucky enough to catch. The present implementation of Wraith Scheme will set no records for high performance and will overwhelm no one with exotic features, but a reasonable, inexpensive Scheme implementation, on a computer as widely used and as easy to use as the Macintosh, is likely to be interesting to computer hobbyists and enthusiasts, and perhaps even useful for students and educators. So enjoy. And pass it on.

      And if you are feeling guilty about not sending me a shareware donation, send me some EMail, and tell me how much you like Wraith Scheme. Or if you don't like it, tell me why not and what I can do about it.

    • "Seriously, how much do you want for a shareware donation?"

      As I said before, there is an old Russian proverb: "Hope for the best, and expect the worst; thus you shall never be disappointed."

    • "What's it like, being a shareware developer?"

      It's poor for the wallet but great for the ego. During the first few months after the initial release of Pixie Scheme I barely received enough shareware donations to buy dinner in a cheap place. By dividing the total shareware donations I received by the time I spent on the project, I find I was working for about two cents an hour -- a rate that Scrooge McDuck might have paid. But for a month or so after each release, I made an offer over the "usenet" computer network, to provide a copy of the program to anyone who mailed me a disk, a return mailer, and sufficient postage. I received requests from places as remote as Finland and New Zealand: Pixie Scheme truly achieved world-wide distribution, long before the Internet came of age. I was also pleased to find that the Boston Computer Society Macintosh Users Group (BMUG) had put the first release of Pixie Scheme on one of their "developer" disks, and had also included it on a CD ROM of public domain and shareware software.

      What's more, it turned out that a modest proportion of the folks who did send me a shareware donation didn't send just the dollar that I suggested for Pixie Scheme; frequently they sent as much as ten dollars. Thus I can demonstrate on the basis of actual experience that my software was worth more money than I asked for it. Who else in the software-development industry can make such a claim? Usually it is very much the other way around.

    • "Are you, or were you an 'official' Apple developer?"

      I was an "Apple Associate" during the latter part of the time I was working on Pixie Scheme. I was an Apple developer with an ADC "Select" membership from 2007 till 2010, when Apple changed its developer categories. Now I am an undifferentiated Apple Developer.

    • "What was it like, being an Apple Developer in the old days?"

      Each year they sent out several GBytes of interesting stuff on CD-ROM, as well as lots of printed documents. If I had wanted to upgrade my system, I could have done so at a discount. The folks at Developer Technical Support were very helpful. It took longer than I expected to get a reply to my application to register some of the resource types used in Pixie Scheme. I sent them a product: Perhaps they were more accustomed to vaporware.

    • "How does Pixie Scheme III tie into all this?"

      Pixie Scheme III is a port and downgrade of Wraith Scheme to the Apple iPad™. I was intrigued by the user interface Apple was touting for the iPad -- a Scheme interpreter in that style would look much like the original Pixie Scheme of the 1980s.

      At that time -- June 2010 -- Apple's rules and procedures for App Store applications were quite restrictive, so it seemed unlikely that my prospective port could make it in: I figured it would be just for me to use. That would have been sad, because I believed in public service, and surely any program-development environment for the iPad would be useful to many people. Yet I was intrigued even so.

      I did not then own an iPad, so in order to play with the interface inexpensively and on familiar turf, I first wrote Pixie Scheme II, a Macintosh application with a user interface as close to the iPad style as I could manage to create. I liked its look and feel, so I then began work on the the iPad application itself, testing it on Apple's iPad simulator rather than on an actual iPad. When the app ran well enough on the simulator, I bought an iPad and made it run there as well.

      About that time -- September, 2010 -- Apple changed its App Store rules so that my app might be able to get in. I dithered and made excuses for a month and a half, then submitted it, and it was accepted. Knock on wood ...

      I decided to sell it for a dollar because supporting it well would mean occasionally buying more Apple portable devices to test it on, and also because I was curious about who was using it, and where. Yet Pixie Scheme III is open-source: People with the proper software tools can download it and build the app to run on their own iPads.

    • "Tell us something about yourself."

      Life happens while you are making other plans: I am a retread astrophysicist who has worked as flight instructor, consultant to the U. S. Department of Energy, and computer scientist. My doctoral thesis project used a spacecraft instrument (1975 Apollo-Soyuz Test Project) to study the interstellar medium. My last job before retiring was in the Flight Software Group at SpaceX.

      My introduction to computer science was writing small Fortran programs to reduce thesis data. (I thought they were big Fortran programs at the time.) I have spend most of my career working on parallel processing -- SIMD and MIMD, hardware and software. I once helped build a CyberSpace interface. I have worked in what marketeers call "artificial intelligence" but is better termed "system programming in Lisp". And I have won Rogue many times, without resorting to Wizard mode to do so.

      I am a science fiction fan, an English Regency ballroom dancer, and an occasional bicyclist, rose grower and amateur astronomer. I play guitar and now and then build simple musical instruments. I live in Fillmore, California, with too many cats and not enough time.

      I have a personal web site: http://JayReynoldsFreeman.com.

      I am active on Second Life as "CeeJay Tigerpaw".

      Answers to commonly-asked personal questions: blue, chocolate, chunky, single, and only if the esteem is mutual.

    • "What did you think of the Macintosh then?"

      It was the least unsatisfactory personal computer I knew of, but that was an easy win, considering the competition. It needed a real operating system, decent graphics, and a decent user interface. Programming with the old ToolBox was like trying to wash your hands with rubber cement. And oh, yes, they should have given them away free, so everybody could have had one.

    • "What do you think of the Macintosh now?"

      It is the least unsatisfactory personal computer I know of, but that's an easy win, considering the competition. The operating system, graphics, and user interface are much improved. Programming with the current Apple development environment is still pretty Byzantine, but if you can make it through the maze of twisty little class definitions, interfaces, and secret passageways, all alike, you have the full might and majesty of the entire Byzantine Empire -- that would be Apple -- on your side, and there is something to be said for that. And they still should give them away free, so everybody could have one.

    • "Are you planning any more projects?"

      Well, the garden needs some work, and... oh, you meant programming projects. Let's just say that I have had lots of cats.

    • "Do you have any last bits of wisdom or words of advice for users?"

      Do you believe in users? Never mind. Anyway, remember, "lambda" is spelled with a 'b'. And I was just kidding about Santa Claus.


Thanks To:

    (Pretty much in alphabetical order, except for the cats, who of course have the last word.)

  • Apple Incorporated, for providing excellent software development environments and numerous examples of code, all for free.

  • The members of the Silicon Valley "Cocoaheads" group, for advice about the Wraith Scheme user interface.

  • The creators of the various "Rn" Scheme reports, for providing much clear explanatory text and numerous examples -- both simple and subtle -- of how the language is supposed to work. I am particularly grateful for the example implementations of derived expression types in section 7.3 of the R5 report, which formed the basis of the implementation of most of those types in Wraith Scheme.

  • Mike Deering, for lengthy discussions of tagged-pointer Lisps.

  • Sandy Lerner, for encouragement in development and advice on commercial prospects.

  • Tim May, for humor, and for the chance to play with his computer, software and other toys.

  • Charles Smith, for hiring me into a major artificial-intelligence laboratory.

  • Pixie, Wraith, Socks, Weasel, and numerous other cats, for love and purrs, and for the sharing of their ineffable names.

    Notwithstanding the honest acknowledgments and grateful thanks given above, I am confident that all errors and misfeatures in Wraith Scheme are nobody's fault but mine.


-- Jay Reynolds Freeman, September 2022

Wraith Face