Title

SLIB Prerequisites

Author

Aubrey Jaffer

Status

This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. To provide input on this SRFI, please mail to <srfi minus 96 at srfi dot schemers dot org>. See instructions here to subscribe to the list. You can access previous messages via the archive of the mailing list.

Abstract

This SRFI specifies a set of procedures and macros presenting a uniform interface sufficient to host the SLIB Scheme Library system.

Rationale

SLIB is a portable library for the programming language Scheme. It provides a platform independent framework for using packages of Scheme procedures and syntax. As distributed, SLIB contains useful packages for all Scheme implementations. Its catalog can be transparently extended to accomodate packages specific to a site, implementation, user, or directory.

This SRFI provides the uniform interface which SLIB code calls in order to execute its library system. Parameters and capabilities of the hosting implementation are also captured.

Procedures for certain operations beyond the capabilities of R5RS are defined by this SRFI. Those procedures simply return `#f' if they are not supported by the hosting implementation, allowing the calling program to detect and take appropriate action.

SLIB was first released in 1992. Common-Lisp, then as now, incorporates many practical facilities in a straightforward and consistent fashion, which the Scheme reports do not. Given that ANSI-X3.226 Common-Lisp is widely adopted and in the same family of languages as Scheme, it makes sense to incorporate many of Common-Lisp's constructs for these facilities.

Configuration

software-type is inherited from Common-Lisp. In SLIB it is used primarily for dealing with the file system and commands executed by the system call.

Operating-System Names

It would be good to have a definitive set of operating-system names for software-type. RFC-3232 says that the operating-system list which originated in RFC-952 and ended with RFC-1700 is no longer defined by a RFC:
Since 1994, this sequence of RFCs have been replaced by an online database accessible through a web page (currently, www.iana.org). The purpose of the present RFC is to note this fact and to officially obsolete RFC 1700, whose status changes to Historic. RFC 1700 is obsolete, and its values are incomplete and in some cases may be wrong.

We expect this series to be revived in the future by the new IANA organization.

That was in January 2002. The revival has not occurred. The web page seems to be http://www.iana.org/assignments/operating-system-names. Last updated 2002-04-29, it is six years out-of-date at this writing. Even for the operating-systems it does address, it isn't consistent. Some operating-systems appear without version; some appear only with versions; some appear both ways. MSDOS appears without a hyphen or version while the entries for SunOS are SUN-OS-3.5, SUN-OS-4.0, and SUN. For MicroSoft Windows there are 12 varieties (none with MS or Microsoft), all with versions separated by hypens and a lone version-less WIN32.

scheme-implementation-home-page is informational; it is useful for finding documentation and filing bug reports.

scheme-file-suffix is needed because `.' is not the suffix separator on some file-systems (eg. NOSVE). scheme-file-suffix would have been more appropriately named slib-source-file-suffix. It is not advertised to SLIB users and used only for loading SLIB files.

slib:features was originally *features* (a la Common-Lisp); it was changed to avoid conflict with Guile, probably a bad decision.

If a Scheme implementation has a feature close to SLIB's, perhaps differing only in names or argument order, it is good practice to put wrapper functions making them compatible in the SRFI-96 file and add the feature to slib:features.

most-positive-fixnum is a hybrid of most-positive-fixnum and array-dimension-limit from Common-Lisp. char-code-limit is also from Common-Lisp.

File-System

The procedure with-load-pathname is an extension to SRFI 59: Vicinity affecting program-vicinity.

program-vicinity, in combination with the "slib:" loaders, enables code within a file to load or access other files in the same directory irrespective of the current working directory, and without needing absolute pathnames. Thus the code for a module can be split among several files as exemplified by slib/macwork.scm, which contains:

(slib:load (in-vicinity (program-vicinity) "mwexpand"))
...
(slib:load (in-vicinity (program-vicinity) "mwdenote"))
(slib:load (in-vicinity (program-vicinity) "mwsynrul"))

A file's vicinity can also be captured during loading for use while running:

(define jacal-vicinity (program-vicinity))
...
(define (terms) (paginate-file (in-vicinity jacal-vicinity "COPYING")))
(define (help) (paginate-file (in-vicinity jacal-vicinity "HELP")))

tmpnam is similar to the POSIX function of the same name. It is essential in some scripting situations (calling other programs using the system procedure) on a multi-user file-system. The Linux manual page for tmpnam deprecates its use; but its recommendation to instead use mkstemp belies the manual-page author's understanding of scripting.

file-exists? and delete-file are the obvious procedures designated by their names. Although they can always return `#f', there are modules in SLIB which will not work correctly in that case. Full implementation of file-exists? and delete-file are strongly encouraged.

Input/Output

open-file, close-port, port?, and call-with-open-ports support both binary and non-binary files without doubling the number of file-related procedures (open-binary-input-file, open-binary-output-file, ...).

call-with-open-ports gives the functionality of call-with-input-file and call-with-output-file for arbitray numbers of mixed input and output files by passing in open ports rather than filenames.

current-error-port is the diagnostic output port called stderr in C parlance. If the system doesn't support it, then current-error-port can just be set to current-output-port.

force-output is inherited from Common-Lisp. If the system doesn't support it, then it can just do nothing.

file-position is inherited from Common-Lisp. It is active (versus stubbed to return #f) for 7 of SLIB's 20 implementations.

output-port-width and output-port-height are metrics for glass-teletype style output.

Defmacro

defmacro, gentemp, and macroexpand are inherited from Common-Lisp.

macroexpand as specified by this SRFI is free to expand macros or other syntax which was not defined by defmacro.

SLIB uses defmacro:load to load module source files which use defmacro. defmacro:eval can be passed as the argument to the repl:top-level procedure in the repl package to create a read-eval-print loop which understands defmacro, even when the underlying implementation does not.

R5RS Macros

If the macro feature is provided, then the procedures macro:expand, macro:eval, and macro:load must be defined.

Of these, only macro:load is used (for loading packages) by SLIB code.

macro:eval would typically be used with the repl:top-level procedure in the repl package to create a macro-enabled read-eval-print loop.

macro:expand is indispensable for debugging R5RS Macros; but implementations are not required to make it functional, in which case macro:expand will always return #f.

System

Primarily because of with-load-pathname, SLIB-specific varieties of load are required: slib:load-source, slib:load-compiled, and slib:load.

slib:eval is the single-argument eval from Common-Lisp. slib:eval-load is used to create loaders with a given eval argument.

slib:warn and slib:error print out their arguments, preferably to current-error-port.

slib:exit returns if it doesn't work.

Web browsers have become so ubiquitous that programming languagues should support a simple uniform interface to them.  If an implementation can't invoke a browser, then browse-url returns `#f'.

Miscellany

R4RS and R5RS mention #\newline and #\space, but not other specific whitespace characters. slib:tab and slib:form-feed are needed for formatted output.

When passing functions for mapping, the identity function, (lambda (obj) obj), often arises. identity is defined to be that function.

Mutual exclusion primitives such as the Dijkstra semaphore and the mutex of SRFI-18 and SRFI-21 combine an atomic operation with a wait mechanism. Wait mechanisms such as calling a scheduler continuation can be expressed in Scheme; only the atomic operation is novel. So this SRFI introduces make-exchanger, returning a procedure which performs an atomic swap between its argument and an internal location.

t, nil, and last-pair are from older Scheme-Reports. Too few for their own module, SLIB's defining them could provoke redefinition warnings; so they are part of SRFI-96.

Specification

Configuration

These constants and procedures describe characteristics of the Scheme and host operating system.

Function: software-type
Returns a symbol denoting the generic operating system type. For instance, unix, vms, macos, amiga, or ms-dos. The software-type of Linux is unix. The software-type of MS-Windows and Vista is ms-dos.

Function: scheme-implementation-type
Returns a symbol denoting the name of the Scheme implementation.

Function: scheme-implementation-version
Returns a string describing the version of the running Scheme implementation.

Function: scheme-implementation-home-page
Returns #f or a string containing the URI of the Scheme implementation's home page.

Function: scheme-file-suffix
Returns a string with the suffix of SLIB Scheme files in the library-vicinity on this system; typially it is ".scm".

Variable: slib:features
slib:features is a list of symbols naming the (SLIB) features supported by this implementation. `vicinity' must be an element of slib:features.

If the `srfi-0' feature is provided, then slib/require.scm will add any SRFIs from 0 to 150 discovered using `cond-expand'. slib/require.scm will also test for and provide (if appropriate) the features `inexact', `rational', `real', `complex', and `bignum'.

There are features which can't be provided by portable Scheme source code. They can be made members of `slib:features' initially or supplied after initialization using (provide 'feature).
featuredescription
sourcecan load Scheme source files (slib:load-source filebase)
compiledcan load compiled files (slib:load-compiled filebase)
char-ready?has R4RS, R5RS char-ready?
object-hashhas R2RS object-hash and object-unhash
full-continuationcan return multiple times
ieee-floating-pointinexact numbers conform to IEEE Standard 754-1985 IEEE Standard for Binary Floating-Point Arithmetic
sicpthis implementation runs code from Structure and Interpretation of Computer Programs by Abelson and Sussman.
edtext editor is invoked by (ed) or (ed filename)
systemposix (system string)
getenvposix (getenv string)
program-arguments(program-arguments) returns a list of the program name followed by the command-line argument strings
current-time(current-time) returns the time in seconds since 1/1/1970

If a feature is provided whose name is the same as an SLIB module, then it will provide the procedures and syntax as documented for that SLIB module.

If the feature `r5rs' is provided, then the following features should also be provided:
eval R5RS two-argument eval
values R5RS values and call-with-values
dynamic-wind R5RS dynamic-wind
macro (R5RS syntax-rules macros) has define-syntax, let-syntax, letrec-syntax, syntax-rules, macro:expand, macro:eval, and macro:load
delay has delay and force
multiarg-apply apply can take more than 2 args
char-ready? has char-ready? procedure
rev4-optional-procedureshas list-tail, string-copy, string-fill!, and vector-fill!

Constant: most-positive-fixnum
In implementations which support integers of practically unlimited size, most-positive-fixnum is a large exact integer within the range of exact integers that may result from computing the length of a list, vector, or string.

In implementations which do not support integers of practically unlimited size, most-positive-fixnum is the largest exact integer that may result from computing the length of a list, vector, or string.

Constant: char-code-limit
An integer 1 larger that the largest value which can be returned by char->integer.

File-System

SRFI 59: Vicinity as extended by with-load-pathname must be supported.

Function: with-load-pathname path thunk
path should be a string naming a file being read or loaded. with-load-pathname evaluates thunk in a dynamic scope where an internal variable is bound to path; the internal variable is used for messages and program-vicinity. with-load-pathname returns the value returned by thunk.

Function: tmpnam
Returns a pathname for a file which will not be used by any other process. Successive calls to (tmpnam) will return distinct pathnames.

Function: file-exists? filename
Returns #t if the specified file exists. Otherwise, returns #f. If the underlying implementation does not support this feature then #f is always returned.

Function: delete-file filename
Deletes the file specified by filename. If filename can not be deleted, #f is returned. Otherwise, #t is returned.

Input/Output

Function: open-file filename modes
filename should be a string naming a file. open-file returns a port depending on the symbol modes:
r
an input port capable of delivering characters from the file.
rb
a binary input port capable of delivering characters from the file.
w
an output port capable of writing characters to a new file by that name.
wb
a binary output port capable of writing characters to a new file by that name.

If an implementation does not distinguish between binary and non-binary files, then it must treat rb as r and wb as w.

If the file cannot be opened, either #f is returned or an error is signalled. For output, if a file with the given name already exists, the effect is unspecified.

Function: port? obj
Returns #t if obj is an input or output port, otherwise returns #f.

Procedure: close-port port
Closes the file associated with port, rendering the port incapable of delivering or accepting characters.

close-file has no effect if the file has already been closed. The value returned is unspecified.

Function: call-with-open-ports proc ports ...
Function: call-with-open-ports ports ... proc
Proc should be a procedure that accepts as many arguments as there are ports passed to call-with-open-ports. call-with-open-ports calls proc with ports .... If proc returns, then the ports are closed automatically and the value yielded by the proc is returned. If proc does not return, then the ports will not be closed automatically unless it is possible to prove that the ports will never again be used for a read or write operation.

Function: current-error-port
Returns the current port to which diagnostic and error output is directed.

Procedure: force-output
Procedure: force-output port
Forces any pending output on port to be delivered to the output device and returns an unspecified value. The port argument may be omitted, in which case it defaults to the value returned by (current-output-port). If the implementation does not support it, then force-output has no effect.

Function: file-position port
port must be open to a file. file-position returns the current position of the character in port which will next be read or written. If the implementation does not support file-position, then #f is returned.

Function: file-position port k
port must be open to a file. file-position sets the current position in port which will next be read or written. If successful, #f is returned; otherwise file-position returns #f.

An implementation not supporting file-position can define it thus:

(define (file-position port . k) #f)

Function: output-port-width
Function: output-port-width port

Returns the width of port, which defaults to (current-output-port) if absent. If the width cannot be determined 79 is returned.

Function: output-port-height
Function: output-port-height port

Returns the height of port, which defaults to (current-output-port) if absent. If the height cannot be determined 24 is returned.

Defmacro

Macro: defmacro name lambda-list form ...
When encountered by defmacro:eval, defmacro:macroexpand*, or defmacro:load defines a new macro which will henceforth be expanded when encountered by defmacro:eval, defmacro:macroexpand*, or defmacro:load.

Function: gentemp
Returns a new (interned) symbol each time it is called. The symbol names are implementation-dependent
(gentemp) => scm:G0
(gentemp) => scm:G1

Function: defmacro:eval e
Returns the value of evaluating scheme expression e where all its defmacros have been expanded.

Function: defmacro:load filename
filename should be a string. If filename names an existing file, the defmacro:load procedure reads Scheme source code expressions and definitions from the file and evaluates them sequentially. These source code expressions and definitions may contain defmacro definitions. The defmacro:load procedure does not affect the values returned by current-input-port, current-error-port, and current-output-port.

Function: macroexpand form
If form is a defmacro call, macroexpand will repeatedly expand the form until it is no longer a defmacro call. A form is considered to be a defmacro call if it is a cons whose car is a symbol for which a defmacro has been defined.
If macroexpand expands all defmacros, not just the one at top-level, then the implementation should provide the feature: defmacroexpand and:
(define defmacro:expand* defmacroexpand)

defmacro:expand* is specified thus:

Function: defmacro:expand* e
Returns the result of expanding all defmacros in scheme expression e.

R5RS Macros

If the macro feature is provided, then the procedures macro:expand, macro:eval, and macro:load will be defined.

Function: macro:expand sexpression
Takes an expression, macro-expands it, and returns the result of the macro expansion. If an implementation does not support macro:expand, then #f is returned.

Function: macro:eval sexpression
Takes an expression, macro-expands it, evals the result of the macro expansion, and returns the result of the evaluation.

Procedure: macro:load filename
filename should be a string. If filename names an existing file, the macro:load procedure reads Scheme source code expressions and definitions from the file and evaluates them sequentially. These source code expressions and definitions may contain macro definitions. The macro:load procedure does not affect the values returned by current-input-port, current-error-port, and current-output-port.

System

Procedure: slib:load-source name
Loads a file of Scheme source code from name with the default filename extension used in SLIB. For instance if the filename extension used in SLIB is `.scm' then (slib:load-source "foo") will load from file `foo.scm'.

Procedure: slib:load-compiled name
On implementations which support separtely loadable compiled modules, loads a file of compiled code from name with the implementation's filename extension for compiled code appended.

Procedure: slib:load name
Loads a file of Scheme source or compiled code from name with the appropriate suffixes appended. If both source and compiled code are present with the appropriate names then the implementation will load just one. It is up to the implementation to choose which one will be loaded.

If an implementation does not support compiled code then slib:load will be identical to slib:load-source.

Procedure: slib:eval obj
slib:eval returns the value of obj evaluated in the current top level environment.

Procedure: slib:eval-load filename eval
filename should be a string. If filename names an existing file, the Scheme source code expressions and definitions are read from the file and eval called with them sequentially. The slib:eval-load procedure does not affect the values returned by current-input-port, current-error-port, and current-output-port.

Procedure: slib:warn arg1 arg2 ...
Outputs (preferably to current-error-port) a warning message containing its arguments.

Procedure: slib:error arg1 arg2 ...
Outputs (preferably to current-error-port) an error message containing its arguments, aborts evaluation of the current form, and responds in a system dependent way to the error. Typical responses are to abort the program or to enter a read-eval-print loop.

Procedure: slib:exit n
Procedure: slib:exit
Exits from the Scheme session returning status n to the system. If n is omitted or #t, a success status is returned to the system (if possible). If n is #f a failure is returned to the system (if possible). If n is an integer, then n is returned to the system (if possible). If the Scheme session cannot exit, then an unspecified value is returned from slib:exit.

Function: browse-url url
If a browser is running, browse-url causes the browser to display the page specified by string url and returns `#t'.

If the browser is not running, browse-url starts a browser displaying the argument url. If the browser starts as a background job, browse-url returns `#t' immediately; if the browser starts as a foreground job, then browse-url returns `#t' when the browser exits; otherwise (if no browser) it returns `#f'.

If (provided? 'getenv):

Function: getenv name
Looks up name, a string, in the program environment. If name is found a string of its value is returned. Otherwise, #f is returned.

If (provided? 'system):

Function: system command-string
Executes the command-string on the computer and returns the integer status code.

If (provided? 'program-arguments):

Function: program-arguments
Returns a list of strings, the first of which is the program name followed by the command-line arguments.

Miscellany

Function: identity x
identity returns its argument.

Example:

(identity 3)
   => 3
(identity '(foo bar))
   => (foo bar)
(map identity lst)
   == (copy-list lst)

Constant: slib:tab
The tab character.

Constant: slib:form-feed
The form-feed character.

Mutual Exclusion

An exchanger is a procedure of one argument regulating mutually exclusive access to a resource. When a exchanger is called, its current content is returned, while being replaced by its argument in an atomic operation.

Function: make-exchanger obj

Returns a new exchanger with the argument obj as its initial content.

The following code implements Dijkstra semaphores in terms of make-exchanger. If run-other-process does nothing, then P! and V! implement spin-locks. Normally, run-other-process would be a scheduler which would capture its continuation using call-with-current-continuation and run pending process continuations, eventually running this captured continuation.

;;; Init(Semaphore s, Integer v)
;;; {
;;;   s := v;
;;; }
(define (make-semaphore v) (make-exchanger v))

;;; P(Semaphore s)
;;; {
;;;   wait until s > 0, then s := s-1;
;;;   /* must be atomic once s > 0 is detected */
;;; }
(define (P! s)
  (let loop ()
    (define val (s #f))
    (cond ((and val (positive? val))
	   (s (- val 1)))
	  (else
	   (and val (s val))
	   (run-other-process)
	   (loop)))))

;;; V(Semaphore s)
;;; {
;;;   s := s+1;   /* must be atomic */
;;; }
(define (V! s)
  (let loop ()
    (define val (s #f))
    (cond (val
	   (s (+ val 1)))
	  (else
	   (run-other-process)
	   (loop)))))

Legacy

The following defines were present in Scheme until R4RS (see section `Language changes' in Revised(4) Scheme).

Constant: t
Defined as #t.

Constant: nil
Defined as #f.

Function: last-pair l
Returns the last pair in the list l. Example:
(last-pair (cons 1 2))
   => (1 . 2)
(last-pair '(1 2))
   => (2)
    == (cons 2 '())

Deployment

The SLIB distribution unpacks into library files (ending with .scm) and SRFI-96 files (ending with .init) in a directory named slib.

Initialization

The SLIB `require' module implements the procedure-based library system whose modules are contained within these files. At the end of the SRFI-96 files this `require' module is loaded, initializing SLIB:
(slib:load (in-vicinity (library-vicinity) "require"))

Implementation

Here are the implementations of SRFI-96, including SRFI-59, for many R4RS and R5RS Scheme implementations. slib/Template.scm is to use as a starting point for a new .init file.
Scheme implementation scheme-implementation-type SRFI-59 & SRFI-96 implementation
Implementation Template slib/Template.scm
Bigloo Bigloo slib/bigloo.init
Chez chez slib/chez.init
ELK 3.0 Elk slib/elk.init
Gambit gambit slib/gambit.init
Guile guile slib/guile.init
JScheme JScheme slib/jscheme.init
Kawa kawa slib/kawa.init
Larceny larceny larceny/lib/SRFI/srfi-59.sch & larceny/lib/SRFI/srfi-96.sch
MacScheme MacScheme slib/macscheme.init
MIT/GNU Scheme MITScheme slib/mitscheme.init
PLT Scheme |MzScheme| slib/mzscheme.init
Pocket Scheme Pocket-Scheme slib/pscheme.init
RScheme RScheme slib/RScheme.init
Scheme->C Scheme->C slib/scheme2c.init
Scheme48 Scheme48 slib/scheme48.init
SCM scm slib/scm.init (scm/Init***.scm)
SCM Mac scm slib/scm.init (scm/Init***.scm)
scsh Scsh slib/scsh.init
sisc sisc slib/sisc.init
STk |STk| slib/STk.init
T3.1 T slib/t3.init
umb-scheme umb-scheme slib/umbscheme.init
VSCM Vscm slib/vscm.init

Copyright

Copyright (C) Aubrey Jaffer 2007, 2008. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: Donovan Kolbly
Last modified: Fri Jun 6 07:40:21 CDT 2008