As real-world programmers develop large software systems, they commonly find that certain subproblems (such as sorting, line-at-a-time input and output, and the implementation of more advanced data structures like hash tables and priority queues) commonly recur. Sensible programmers naturally prefer to write up one careful and exact solution to a recurring subproblem and to store it so that it can easily be reloaded, rather than repeatedly writing a new solution, or a new version or copy of the solution, for each recurrence of the subproblem. A library is a collection of such solutions.
The design of the Scheme programming language promotes the development and use of libraries. From the point of view of the language, a library is a program that happens to consist entirely of definitions. One can, in effect, extend the language to include the procedures that are defined in a library simply by loading the file in which the library is stored; no special steps are required to merge or link the file thus loaded with the Scheme interactive interface.
In a company that develops and sells software, the procedure library (or at least the source code from which it is compiled) is usually a secret; only employees of the company may see and use it. However, Scheme programmers working in academic settings sometimes make their libraries available. A sampling of what is available can be found in the Internet Scheme Repository at Indiana University.
As an example, let's look at one very popular library: SLIB, compiled and
edited by Aubrey
Jaffer. Though copyrighted, SLIB is available without charge over the
Internet. It is available on MathLAN in the directory
/usr/local/lib/slib. The SLIB manual is
accessible on the World Wide Web, beginning at
http://www-swiss.ai.mit.edu/~jaffer/slib_toc.html.
SLIB is not written entirely in standard Scheme. Some of the contributions use added features that differ slightly from one Scheme implementation to another or are not provided at all by some versions of Scheme. Each SLIB user is therefore encouraged to start by loading a configuration file for his or her particular Scheme system; the configuration file conceals these differences by adding appropriate definitions for missing procedures and redefining others so that they match the expectations of the SLIB libraries. The configuration file for Chez Scheme is chez.init.
Start Scheme and have it load in the configuration file. If you're
starting Scheme in an dtterm window, you can do this by adding the
name of the configuration file to the command line. Alternatively, either
in dtterm or in an XEmacs window, you can call the
load procedure to bring in the file.
The source code in the SLIB library is divided into a large number of
files, but it is actually easier to understand the organization of SLIB by
thinking of it as a collection of optional features that can be added to
standard Scheme. Once the configuration file has been loaded, you can get
a list of the features that are available in SLIB by evaluating the
expression (map car *catalog*). (*Catalog* is an
association list that SLIB uses internally; the keys are the features
supplied, and the values are mostly the names of the files in which those
features are implemented.)
Exactly one of the symbols replicate, tree,
and boolean-functions designates a feature that
is provided by SLIB. Find out which one.
The configuration file also arranges for a non-standard procedure named
require to be defined. The require procedure
takes a symbol as argument; when invoked, it looks up the symbol in
*catalog* and opens and loads in the file that supplies the
feature -- unless it has previously been loaded, in which case that step is
omitted. Sometimes one SLIB source-code file presupposes another; the
require procedure ensures that all the necessary components
are loaded and that none of them is loaded more than once.
Here's a simple example of the use of an SLIB procedure. Mail-order
companies and other businesses that take customers' names by telephone
sometimes have trouble with misspellings of names. If a customer's name is
recorded incorrectly on one document, such as an order form, it may be
difficult to match that document up with others on which the name is
correctly spelled, such as a payment voucher. To make it easier to recover
from such a mismatch, such companies sometimes add to their internal
documents a four-character ``Soundex'' code, derived from the name, that is
often identical for differently spelled variants of a name. For instance,
Schultz and Shults have the same Soundex code
(S432); so do Berowne and Braun
(B650), and Macleod and McCloud
(M243).
One of SLIB's features is soundex; if you enter the expression
(require 'soundex), SLIB will load all the procedures
necessary to define a procedure, also called soundex, that
takes any string as argument and delivers a Soundex code for it.
Determine the Soundex code for your last name and the Soundex code for any of the various misspellings of your last name that correspondents have perpetrated.
Using a different feature of SLIB, find the prime factors of 1522301997.
Since SLIB is provided in the form of source-code files, one can examine them to study exactly what the library procedures do, how they do it, and how real-world Scheme programmers use the language. You'll find all of the library files in the /usr/local/lib/slib directory; the file names end in .scm.
Some of the SLIB source-code files make use of an optional syntactic
construction that I've avoided so far in this course: In defining a
procedure by associating the identifier with the value of a
lambda-expression, one can abbreviate the definition by
enclosing the procedure identifier and the elements of the parameter list
in parentheses and omitting the lambda. For instance, instead
of writing
(define square
(lambda (n)
(* n n)))
one could write
(define (square n) (* n n))
so that one would have slightly fewer lines of text and pairs of parentheses to worry about. The two forms of procedure definition are completely equivalent; the choice of one or the other is a matter of personal style and preference.
Definitions of variable-arity procedures also have short forms. For instance,
(define writeln
(lambda args
(for-each display args)
(newline)))
has the short form
(define (writeln . args) (for-each display args) (newline))
and
(define average
(lambda (first . rest)
(/ (+ first (apply + rest)) (+ 1 (length rest)))))
has the short form
(define (average first . rest) (/ (+ first (apply + rest)) (+ 1 (length rest))))
Examine one or more of the SLIB course files and choose one to study carefully. (If you can't decide, strcase.scm might be a good choice.) What features of the author's programming style make the code more readable and seem to be worth emulating? What features make it harder to read and understand the code?
This document is available on the World Wide Web as
http://www.math.grin.edu/courses/Scheme/spring-1998/libraries.html
created April 30, 1997
last revised June 21, 1998