This repository has been archived on 2022-08-10. You can view files and clone it, but cannot push or open issues or pull requests.
chez-openbsd/csug/libraries.stex

1220 lines
48 KiB
Text
Raw Normal View History

2022-07-29 15:12:07 +02:00
% Copyright 2005-2017 Cisco Systems, Inc.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
\chapter{Libraries and Top-level Programs\label{CHPTLIBRARIES}}
\index{libraries}%
\index{top-level-programs}%
The Revised$^6$ Report describes two units of portable code:
libraries and top-level programs.
A library is a named collection of bindings with a declared set of
explicitly exported bindings, a declared set of imported libraries, and a
body that initializes its bindings.
A top-level program is a stand-alone program with a declared set of
imported libraries and a body that is run when the top-level program is
run.
The bindings in a library are created and its initialization code run only
if the library is used, directly or indirectly, by a top-level program.
\index{\scheme{import}}%
The \scheme{import} declarations appearing within libraries and top-level
programs serve two purposes: first, they cause the imported libraries to
be loaded, and second, they cause the bindings of the imported libraries
to become visible in the importing library or top-level program.
Libraries are typically stored in the file system, with one library per
file, and the library name typically identifies the file-system path to
the library, possibly relative to a default or programmer-specified set of
library locations.
The exact mechanism by which top-level programs are run and libraries are
loaded is implementation-dependent.
This chapter describes the mechanisms by which libraries and programs are
loaded in {\ChezScheme} along with various features for controlling and
tracking this process.
It also describes the set of built-in libraries and syntactic forms for
defining new libraries and top-level programs outside of a library or
top-level program file.
% \section{Locating Libraries\label{SECTLOCATINGLIBRARIES}}
%
% In {\ChezScheme}, a library to be loaded implicitly by \scheme{import}
% must reside in a file whose name reflects the name of the library.
% For example, if the library's name is \scheme{(tools sorting)}, the
% base name of the file must be \scheme{sorting} with a valid extension, and
% the file must be in a directory named \scheme{tools} which itself resides
% in one of the directories searched by \scheme{import}.
% The set of directories searched by \scheme{import} is determined by
% the
% \index{\scheme{library-directories}}\scheme{library-directories}
% parameter, and the set of
% extensions is determined by the
% \index{\scheme{library-extensions}}\scheme{library-extensions}
% parameter.
% So, if \scheme{(library-directories)} contains the pathnames
% \scheme{"/usr/lib/scheme/libraries"} and \scheme{"."}, and
% \scheme{(library-extensions)} contains the extensions \scheme{.ss}
% and \scheme{.sls}, the path of the \scheme{(tools sorting)}
% library must be one of the following.
%
% \schemedisplay
% /usr/lib/scheme/libraries/tools/sorting.ss
% /usr/lib/scheme/libraries/tools/sorting.sls
% ./tools/sorting.ss
% ./tools/sorting.sls
% \endschemedisplay
%
% A file containing a library or set of libraries can be explicitly loaded
% via \scheme{load}, in which case the file may have any name and may reside
% anywhere in the file system.
%
% \index{\scheme{compile-library}}%
% \index{\scheme{compile-imported-libraries}}%
% A file containing a library may be compiled with \scheme{compile-file}
% or \scheme{compile-library}.
% The only difference between the two is that the latter treats the source
% file as if it were prefixed by an implicit \scheme{#!r6rs}.
% Any libraries upon which the library depends must be compiled first.
% This can be done manually or by setting the parameter
% \scheme{compile-imported-libraries} to \scheme{#t} before compiling
% the importing library.
% If one of the libraries imported by the library is subsequently
% recompiled (say because it was modified), the importing library must also
% be recompiled.
\section{Built-in Libraries\label{SECTBUILTINLIBRARIES}}
In addition to the RNRS libraries mandated by the Revised$^6$ Report:
\schemedisplay
(rnrs base (6))
(rnrs arithmetic bitwise (6))
(rnrs arithmetic fixnums (6))
(rnrs arithmetic flonums (6))
(rnrs bytevectors (6))
(rnrs conditions (6))
(rnrs control (6))
(rnrs enums (6))
(rnrs eval (6))
(rnrs exceptions (6))
(rnrs files (6))
(rnrs hashtables (6))
(rnrs io ports (6))
(rnrs io simple (6))
(rnrs lists (6))
(rnrs mutable-pairs (6))
(rnrs mutable-strings (6))
(rnrs programs (6))
(rnrs r5rs (6))
(rnrs records procedural (6))
(rnrs records syntactic (6))
(rnrs records inspection (6))
(rnrs sorting (6))
(rnrs syntax-case (6))
(rnrs unicode (6))
\endschemedisplay
\index{\scheme{(chezscheme)} library}%
\index{\scheme{(chezscheme csv7)} library}%
\index{\scheme{(scheme)} library}%
\index{\scheme{(scheme csv7)} library}%
{\ChezScheme} also provides two additional libraries: \scheme{(chezscheme)}
and \scheme{(chezscheme csv7)}.
The former can also be referenced as \scheme{(scheme)} and the latter can
also be referenced as \scheme{(scheme csv7)}.
The \scheme{(chezscheme)} library exports bindings for every identifier whose
binding is described in this document, including those for keywords like
\scheme{lambda}, auxiliary keywords like \scheme{else}, module names
like \scheme{scheme}, and procedure names like \scheme{cons}.
In most cases where an identifier exported from the
\scheme{(chezscheme)} library corresponds to an identifier exported from
one of the RNRS libraries, the bindings are identical.
In some cases, however, the \scheme{(chezscheme)} bindings extend the
\scheme{rnrs} bindings in some way.
For example, the \scheme{(chezscheme)} \scheme{syntax-rules} form allows
its clauses to have fenders (Section~\ref{SECTSYNTAXRULES}), while the
\scheme{(rnrs)} \scheme{syntax-rules} form does not.
Similarly, the \scheme{(chezscheme)} \scheme{current-input-port} procedure
accepts an optional \var{port} argument that, when specified, sets the
current input port to \var{port} (Section~\ref{SECTIOINPUT}), while the
\scheme{(rnrs)} \scheme{current-input-port} procedure does not.
When the \scheme{(chezscheme)} library extends an RNRS binding in some
way, the \scheme{(chezscheme)} library also exports the RNRS version,
with the name prefixed by \scheme{r6rs:}, e.g., \scheme{r6rs:syntax-rules}
or \scheme{r6rs:current-input-port}.
The \scheme{(chezscheme csv7)} Version~7 backward compatibility library
contains bindings for a set of syntactic forms and procedures whose syntax
or semantics directly conflicts with the RNRS bindings for the same
identifiers.
The following identifiers are exported from \scheme{(chezscheme csv7)}.
\schemedisplay
record-field-accessible?
record-field-accessor
record-field-mutable?
record-field-mutator
record-type-descriptor
record-type-field-decls
record-type-field-names
record-type-name
record-type-symbol
\endschemedisplay
The bindings of this library should be used only for old code; new
code should use the RNRS variants.
Each of these is also available in the \scheme{(chezscheme)} library with
the prefix \scheme{csv7:}, e.g., \scheme{csv7:record-type-name}.
The interaction environment in which code outside of a library or
RNRS top-level program is scoped contains all of the bindings of the
\scheme{(chezscheme)} library, as described in
Section~\ref{SECTUSEINTERACTIONENVIRONMENT}.
\section{Running Top-level Programs\label{SECTRUNNINGTOPLEVELPROGRAMS}}
\index{\scheme{scheme-script}}%
\index{\scheme{--program} command-line option}%
\index{\scheme{load-program}}%
A top-level program must reside in its own file, which may have any
name and may reside anywhere in the file system.
A top-level program residing in a file is run by one of three mechanisms:
the \scheme{scheme-script} command, the \scheme{--program} command-line
argument, or the \scheme{load-program} procedure.
The \scheme{scheme-script} command is used as follows:
\schemedisplay
scheme-script \var{program-filename} \var{arg} \dots
\endschemedisplay
It may also be run implicitly on Unix-based systems by placing the line
\schemedisplay
#! /usr/bin/env scheme-script
\endschemedisplay
at the front of the file containing the top-level program, making the
top-level program file executable, and executing the file.
This line may be replaced with
\schemedisplay
#! /usr/bin/scheme-script
\endschemedisplay
with \scheme{/usr/bin} replaced by the absolute path to the directory
containing \scheme{scheme-script} if it is not in \scheme{/usr/bin}.
The first form is recommended in the nonnormative appendices to the
Revised$^6$ Report~\cite{r6rsapps}, and works wherever
\scheme{scheme-script} appears in the path.
The \scheme{--program} command is used similarly with the \scheme{scheme}
or \scheme{petite} executables, either by running:
\schemedisplay
scheme --program \var{program-filename} \var{arg} \dots
petite --program \var{program-filename} \var{arg} \dots
\endschemedisplay
or by including
\schemedisplay
#! /usr/bin/scheme --script
\endschemedisplay
or
\schemedisplay
#! /usr/bin/petite --script
\endschemedisplay
at the front of the top-level program file, making the file executable,
and executing the file.
Again, \scheme{/usr/bin} should be replaced with the absolute path to
the actual directory in which \scheme{scheme} and/or \scheme{petite}
resides, if not \scheme{/usr/bin}.
The \scheme{load-program} procedure, described in
Section~\ref{SECTMISCCOMPILEEVAL}, is used like \scheme{load}:
\schemedisplay
(load-program \var{string})
\endschemedisplay
where \var{string} names the file in which the top-level program resides.
Regardless of the mechanism used, if the opening line is in one of the
forms described above, or more generally, consists of
\scheme{#!} followed by a space or a forward slash, the opening line
is not considered part of the program and is ignored once the Scheme
system starts up and begins to run the program.
Thus, the line may be present even in a file loaded by \scheme{load-program}.
In fact, \scheme{load-program} is ultimately used by the other two
mechanisms described above, via the value of the \scheme{scheme-program}
parameter described in Section~\ref{SECTMISCWAITERS}, and it is
\scheme{load-program} that scans past the \scheme{#!} line, if present,
before evaluating the program.
A top-level program may be compiled with the
\index{\scheme{compile-program}}\scheme{compile-program}
procedure described in Section~\ref{SECTMISCCOMPILEEVAL}.
\scheme{compile-program} copies the \scheme{#!} line from the source
file to the object file, followed by a compiled version of the source
code.
Any libraries upon which the top-level program depends, other than
built-in libraries, must be compiled first via \scheme{compile-file}
or \scheme{compile-library}.
This can be done manually or by setting the parameter
\scheme{compile-imported-libraries} to \scheme{#t} before compiling
the program.
The program must be recompiled if any of the libraries upon which
it depends are recompiled.
A compiled top-level program can be run just like a source top-level
program via each of the mechanisms described above.
\index{\scheme{load-library}}%
In {\ChezScheme}, a library may also be defined in the REPL or placed in a
file to be loaded via \scheme{load} or \scheme{load-library}.
The syntax for a library is the same whether the library is placed in
its own file and implicitly loaded via \scheme{import}, entered into
the REPL, or placed in a file along with other top-level expressions to
be evaluated by \scheme{load}.
A top-level program may also be defined in the REPL or placed in a file
to be loaded via \scheme{load}, but in this case, the syntax is slightly
different.
In the language of the Revised$^6$ Report, a top-level program is merely
an unwrapped sequence of subforms consisting of an \scheme{import} form
and a body, delimited only by the boundaries of the file in which it
resides.
In order for a top-level program to be entered in the REPL or placed in
a file to be evaluated by \scheme{load}, {\ChezScheme} allows top-level
programs to be enclosed in a
\index{\scheme{top-level-program}}\scheme{top-level-program} form.
\section{Library and Top-level Program Forms\label{SECTLIBRARYFORMS}}
%----------------------------------------------------------------------------
\noskipentryheader
\formdef{library}{\categorysyntax}{(library \var{name} \var{exports} \var{imports} \var{library-body})}
\returns unspecified
\listlibraries
\endnoskipentryheader
The \scheme{library} form defines a new library with the specified
name, exports, imports, and body.
Details on the syntax and semantics of the library form are given in
Section~\ref{TSPL:SECTLIBPROGRAMS} of {\TSPLFOUR} and in the Revised$^6$
Report.
Only one version of a library can be loaded at any given time, and an
exception is raised if a library is implicitly loaded via \scheme{import}
when another version of the library has already been loaded.
{\ChezScheme} permits a different version of the library, or a new
instance of the same version, to be entered explicitly into the REPL
or loaded explicitly from a file, to facilitate interactive testing
and debugging.
The programmer should take care to make sure that any code that uses
the library is also reentered or reloaded, to make sure that code
accesses the bindings of the new instance of the library.
\schemedisplay
(library (test (1)) (export x) (import (rnrs)) (define x 3))
(import (test))
(define f (lambda () x))
(f) ;=> 3
(library (test (1)) (export x) (import (rnrs)) (define x 4))
(import (test))
(f) ;=> 3 ; oops---forgot to redefine f
(define f (lambda () x))
(f) ;=> 4
(library (test (2)) (export x) (import (rnrs)) (define x 5))
(import (test))
(define f (lambda () x))
(f) ;=> 5
\endschemedisplay
As with module imports (Section~\ref{SECTSYNTAXMODULES}), a library
\scheme{import} may appear anywhere a definition may appear, including at
top level in the REPL, in a file to be loaded by \scheme{load}, or within
a \scheme{lambda}, \scheme{let}, \scheme{letrec}, \scheme{letrec*},
etc., body.
The same \scheme{import} form may be used to import from both libraries
and modules.
\schemedisplay
(library (foo) (export a) (import (rnrs)) (define a 'a-from-foo))
(module bar (b) (define b 'b-from-bar))
(let () (import (foo) bar) (list a b)) ;=> (a-from-foo b-from-bar)
\endschemedisplay
The \scheme{import} keyword is not visible within a library body
unless the library imports it from the \scheme{(chezscheme)} library.
%----------------------------------------------------------------------------
\entryheader
\formdef{top-level-program}{\categorysyntax}{(top-level-program \var{imports} \var{body})}
\returns unspecified
\listlibraries
\endentryheader
\index{top-level-programs}%
A \scheme{top-level-program} form may be entered into the REPL or placed
in a file to be loaded via \scheme{load}, where it behaves as if its
subforms were placed in a file and loaded via \scheme{load-program}.
Details on the syntax and semantics of a top-level program are given in
Section~\ref{TSPL:SECTLIBPROGRAMS} of {\TSPLFOUR} and in the Revised$^6$
Report.
The following transcript illustrates a \scheme{top-level-program} being
tested in the REPL.
\schemedisplay
> (top-level-program (import (rnrs))
(display "hello!\n"))
hello!
\endschemedisplay
\section{Standalone import and export forms\label{SECTLIBRARYIMPORTEXPORTFORMS}}
Although not required by the Revised$^6$ Report,
{\ChezScheme} supports the use of standalone import and
export forms.
The import forms can appear anywhere other definitions
can appear, including within a \scheme{library} body,
\scheme{module} (Section~\ref{SECTSYNTAXMODULES}) body,
\scheme{lambda} or other local body, and at top level.
The export forms can appear within the definitions of a
\scheme{library} or \scheme{module} body to specify additional
exports for the library or module.
Within a library or top-level program, the keywords for
these forms must be imported from the \scheme{(chezscheme)}
library to be available for use, since they are not
defined in any of the Revised$^6$ Report libraries.
%----------------------------------------------------------------------------
\entryheader
\formdef{import}{\categorysyntax}{(import \var{import-spec} \dots)}
\formdef{import-only}{\categorysyntax}{(import-only \var{import-spec} \dots)}
\returns unspecified
\listlibraries
\endentryheader
An \scheme{import} or \scheme{import-only} form is a definition and can
appear anywhere other definitions can appear, including
at the top level of a program, nested within the bodies of
\scheme{lambda} expressions, and nested within modules
and libraries.
Each \var{import-spec} must take one of the following forms.
\schemedisplay
\var{import-set}
(for \var{import-set} \var{import-level} \dots)
\endschemedisplay
\noindent
The \scheme{for} wrapper and \var{import-level} are described in
Chapter~\ref{TSPL:CHPTLIBRARIES} of {\TSPLFOUR}.
They are ignored by {\ChezScheme}, which determines
automatically the levels at which identifiers must
be imported, as permitted by the Revised$^6$ Report.
This frees the programmer from the obligation
to do so and results in more generality as well as more
precision in the set of libraries actually imported
at compile and run time~\cite{Ghuloum:libraries,Ghuloum:phd}.
An \var{import-set} must take one of the following forms:
\schemedisplay
\var{library-spec}
\var{module-name}
(only \var{import-set} \var{identifier} \dots)
(except \var{import-set} \var{identifier} \dots)
(prefix \var{import-set} \var{prefix})
(add-prefix \var{import-set} \var{prefix})
(drop-prefix \var{import-set} \var{prefix})
(rename \var{import-set} (\var{import-name} \var{internal-name}) \dots)
(alias \var{import-set} (\var{import-name} \var{internal-name}) \dots)
\endschemedisplay
Several of these are specified by the Revised$^6$ Report; the remainder
are {\ChezScheme} extensions, including \var{module-name} and the
\scheme{add-prefix}, \scheme{drop-prefix}, and \scheme{alias} forms.
An \scheme{import} or \scheme{import-only} form makes the specified bindings
visible in the scope in which they appear.
Except at top level, they differ in that \scheme{import} leaves all bindings
except for those shadowed by the imported names visible, whereas \scheme{import-only}
hides all existing bindings, i.e., makes only the imported names visible.
At top level, \scheme{import-only} behaves like \scheme{import}.
Each \var{import-set} identifies a set of names to make visible
as follows.
\begin{description}
\item[\scheme{\var{library-spec}}:]
all exports of the library identified by the Revised$^6$ Report \var{library-spec}
(Chapter~\ref{TSPL:CHPTLIBRARIES}).
\item[\scheme{\var{module-name}}:]
all exports of module named by the identifier \var{module-name}
\item[\scheme{(only \var{import-set} \var{identifier} \dots)}:]
of those specified by \var{import-set}, just \scheme{\var{identifier} \dots}
\item[\scheme{(except \var{import-set} \var{identifier} \dots)}:]
all specified by \var{import-set} except \scheme{\var{identifier} \dots}
\item[\scheme{(prefix \var{import-set} \var{prefix})}:]
all specified by \var{import-set}, each prefixed by \var{prefix}
\item[\scheme{(add-prefix \var{import-set} \var{prefix})}:]
all specified by \var{import-set}, each prefixed by \var{prefix}
(just like \scheme{prefix})
\item[\scheme{(drop-prefix \var{import-set} \var{prefix})}:]
all specified by \var{import-set}, with prefix \var{prefix} removed
\item[\scheme{(rename \var{import-set} (\var{import-name} \var{internal-name}) \dots)}:]
all specified by \var{import-set}, with each identifier \var{import-name}
renamed to the corresponding identifier \var{internal-name}
\item[\scheme{(alias \var{import-set} (\var{import-name} \var{internal-name}) \dots)}:]
all specified by \var{import-set}, with each \var{internal-name} as an alias
for \var{import-name}
\end{description}
The \scheme{alias} form differs from the \scheme{rename} form in that both
\var{import-name} and \var{internal-name} are in the resulting set,
rather than just \var{internal-name}.
It is a syntax violation if the
given selection or transformation cannot be made because of a missing
export or prefix.
An identifier made visible via an import of a module or library is scoped as if its
definition appears where the import occurs.
The following example illustrates these scoping rules, using a local
module \scheme{m}.
\schemedisplay
(library (A) (export x) (import (rnrs)) (define x 0))
(let ([x 1])
(module m (x setter)
(define-syntax x (identifier-syntax z))
(define setter (lambda (x) (set! z x)))
(define z 2))
(let ([y x] [z 3])
(import m (prefix (A) a:))
(setter 4)
(list x a:x y z))) ;=> (4 0 1 3)
\endschemedisplay
\noindent
The inner \scheme{let} expression binds \scheme{y} to the value of
the \scheme{x} bound by the outer \scheme{let}.
The import of \scheme{m} makes the definitions of \scheme{x}
and \scheme{setter} visible within the inner \scheme{let}.
The import of \scheme{(A)} makes the variable \scheme{x} exported
from \scheme{(A)} visible as \scheme{a:x} within the body of the
inner \scheme{let}.
Thus, in the expression \scheme{(list x a:x y z)}, \scheme{x} refers to the
identifier macro exported from \scheme{m} while \scheme{a:x} refers to the
variable \scheme{x} exported from \scheme{(A)} and \scheme{y} and \scheme{z}
refer to the bindings established by the inner \scheme{let}.
The identifier macro \scheme{x} expands into a reference to
the variable \scheme{z} defined within the module.
With local import forms, it is rarely necessary to use the extended
import specifiers.
For example, an abstraction that encapsulates the import and reference
can easily be defined and used as follows.
\schemedisplay
(define-syntax from
(syntax-rules ()
[(_ m id) (let () (import-only m) id)]))
(library (A) (export x) (import (rnrs)) (define x 1))
(let ([x 10])
(module M (x) (define x 2))
(cons (from (A) x) (from M x))) ;=> (1 . 2)
\endschemedisplay
\noindent
The definition of \scheme{from} could use \scheme{import} rather than
\scheme{import-only}, but by using \scheme{import-only} we get feedback
if an attempt is made to import an identifier from a library or
module that does not export the identifier.
With \scheme{import} instead of \scheme{import-only}, the current binding,
if any, would be visible if the library or module does not export the
specified name.
\schemedisplay
(define-syntax lax-from
(syntax-rules ()
[(_ m id) (let () (import m) id)]))
(library (A) (export x) (import (rnrs)) (define x 1))
(let ([x 10])
(module M (x) (define x 2))
(+ (from (A) x) (from M y))) ;=> \var{exception: unbound identifier y}
(let ([x 10] [y 20])
(module M (x) (define x 2))
(+ (lax-from (A) x) (lax-from M y))) ;=> 21
\endschemedisplay
Import visibility interacts with hygienic macro expansion in such a
way that, as one might expect,
an identifier \var{x} imported from a module \var{M} is treated in
the importing context as if the corresponding export identifier had
been present in the import form along with \var{M}.
The \scheme{from} abstraction above works because both \var{M} and \var{id}
appear in the input to the abstraction, so the imported \var{id} captures
the reference to \var{id}.
The following variant of \var{from} also works, because both names are
introduced into the output by the transformer.
\schemedisplay
(module M (x) (define x 'x-of-M))
(define-syntax x-from-M
(syntax-rules ()
[(_) (let () (import M) x)]))
(let ([x 'local-x]) (x-from-M)) ;=> x-of-M
\endschemedisplay
On the other hand, imports of introduced module names do not capture
free references.
\schemedisplay
(let ([x 'local-x])
(define-syntax alpha
(syntax-rules ()
[(_ var) (let () (import M) (list x var))]))
(alpha x)) ;=> (x-of-M local-x)
\endschemedisplay
Similarly, imports from free module names do not capture references
to introduced variables.
\schemedisplay
(let ([x 'local-x])
(define-syntax beta
(syntax-rules ()
[(_ m var) (let () (import m) (list x var))]))
(beta M x)) ;=> (local-x x-of-M)
\endschemedisplay
This semantics extends to prefixed, renamed, and aliased bindings
created by the extended \scheme{import} specifiers \scheme{prefix},
\scheme{rename}, and \scheme{alias}.
The \scheme{from} abstraction
works for variables but not for exported keywords, record names,
or module names, since the output is an expression and may thus appear only where
expressions may appear.
A generalization of this technique is used in the following definition
of \scheme{import*}, which supports renaming of imported bindings and
selective import of specific bindings---without the use of the built-in
\scheme{import} subforms for selecting and renaming identifiers
\schemedisplay
(define-syntax import*
(syntax-rules ()
[(_ m) (begin)]
[(_ m (new old))
(module (new)
(module (tmp)
(import m)
(alias tmp old))
(alias new tmp))]
[(_ m id) (module (id) (import m))]
[(_ m spec0 spec1 ...)
(begin (import* m spec0) (import* m spec1 ...))]))
\endschemedisplay
\noindent
To selectively import an identifier from module or library \scheme{m}, the
\scheme{import*} form expands into an anonymous module that first
imports all exports of \scheme{m} then re-exports only the selected
identifier.
To rename on import the macro expands into an anonymous module that
instead exports an alias (Section~\ref{SECTSYNTAXALIAS}) bound to the new name.
If the output placed the definition of \scheme{new} in the same
scope as the import of \scheme{m}, a naming conflict would arise
whenever \scheme{new} is also present in the interface
of \scheme{m}.
To prevent this, the output instead places the import within a nested
anonymous module and links \scheme{old} and \scheme{new}
by means of an alias for the introduced identifier \scheme{tmp}.
The macro expands recursively to handle multiple import specifications.
Each of the following examples imports \scheme{cons} as \scheme{+} and \scheme{+} as
\scheme{cons}, which is probably not a very good idea.
\schemedisplay
(let ()
(import* scheme (+ cons) (cons +))
(+ (cons 1 2) (cons 3 4))) ;=> (3 . 7)
(let ()
(import* (rnrs) (+ cons) (cons +))
(+ (cons 1 2) (cons 3 4))) ;=> (3 . 7)
\endschemedisplay
% for testing
% (module m (x y z)
% (define x 'x)
% (define y 'y)
% (define z 'z))
%----------------------------------------------------------------------------
\entryheader
\formdef{export}{\categorysyntax}{(export \var{export-spec} \dots)}
\returns unspecified
\listlibraries
\endentryheader
An \scheme{export} form is a definition and can appear with other
definitions at the front of a \scheme{library} or \scheme{module}.
It is a syntax error for an \scheme{export} form to appear in other
contexts, including at top level or among the definitions of a
top-level program or \scheme{lambda} body.
Each \var{export-spec} must take one of the following forms.
\schemedisplay
\var{identifier}
(rename (\var{internal-name} \var{export-name}) \dots)
(import \var{import-spec} \dots)
\endschemedisplay
\noindent
where each \var{internal-name} and \var{export-name} is an identifier.
The first two are syntactically identical to \scheme{library}
\var{export-spec}s, while the third is syntactically
identical to a {\ChezScheme} \scheme{import} form, which is an extension of the
R6RS library \scheme{import} subform.
The first form names a single export, \var{identifier}, whose export
name is the same as its internal name.
The second names a set of exports, each of whose export name is
given explicitly and may differ from its internal name.
For the third, the identifiers identified by the \scheme{import} form
become exports, with aliasing, renaming, prefixing, etc., as specified by the
\var{import-spec}s.
The module or library whose bindings are exported by an \scheme{import}
form appearing within an \scheme{export} form can
be defined within or outside the exporting module or library and need
not be imported elsewhere within the exporting module or library.
The following library exports a two-armed-only variant of \scheme{if}
along with all remaining bindings of the \scheme{(rnrs)} library.
\schemedisplay
(library (rnrs-no-one-armed-if) (export) (import (except (chezscheme) if))
(export if (import (except (rnrs) if)))
(define-syntax if
(let ()
(import (only (rnrs) if))
(syntax-rules ()
[(_ tst thn els) (if tst thn els)]))))
(import (rnrs-no-one-armed-if))
(if #t 3 4) ;=> 3
(if #t 3) ;=> \var{exception: invalid syntax}
\endschemedisplay
Another way to define the same library would be to define the
two-armed-only \scheme{if} with a different internal name and use
\scheme{rename} to export it under the name \scheme{if}:
\schemedisplay
(library (rnrs-no-one-armed-if) (export) (import (chezscheme))
(export (rename (two-armed-if if)) (import (except (rnrs) if)))
(define-syntax two-armed-if
(syntax-rules ()
[(_ tst thn els) (if tst thn els)])))
(import (rnrs-no-one-armed-if))
(if #t 3 4) ;=> 3
(if #t 3) ;=> \var{exception: invalid syntax}
\endschemedisplay
The placement of the \scheme{export} form in the library body is
irrelevant, e.g., the \scheme{export} form can appear after the
definition in the examples above.
%----------------------------------------------------------------------------
\entryheader
\formdef{indirect-export}{\categorysyntax}{(indirect-export \var{id} \var{indirect-id} \dots)}
\returns unspecified
\listlibraries
\endentryheader
This form is a definition and can appear wherever any other definition
can appear.
An \scheme{indirect-export} form declares that the named
\var{indirect-id}s are indirectly exported to top level if \var{id}
is exported to top level.
In general, if an identifier is not directly exported by a library or
module, it can be referenced outside of the library or module only in
the expansion of a macro defined within and exported from the library
or module.
Even this cannot occur for libraries or modules defined at top level
(or nested within other libraries or modules), unless either (1)
the library or module has been set up to implicitly export all
identifiers as indirect exports, or (2) each indirectly exported
identifier is explicitly declared as an indirect export of some
other identifier that is exported, either directly or indirectly, from
the library or module, via an \scheme{indirect-export} or the built-in
indirect export feature of a \scheme{module} export subform.
By default, (1) is true for a library and false for a module, but the
default can be overridden via the \scheme{implicit-exports}
form, which is described below.
This form is meaningful only within a top-level library, top-level module,
or module enclosed within a library or top-level module, although it
has no effect if the library or module already implicitly exports all
bindings.
It is allowed anywhere else definitions can appear, however, so macros
that expand into indirect export forms can be used in any definition
context.
Indirect exports are listed so the compiler can determine the
exact set of bindings (direct and indirect) that must be inserted
into the top-level environment, and conversely, the set of bindings
that may be treated more efficiently as local bindings (and
perhaps discarded, if they are not used).
In the example below, \scheme{indirect-export} is used to indirectly
export \scheme{count} to top level when \scheme{current-count} is
exported to top level.
\schemedisplay
(module M (bump-count current-count)
(define-syntax current-count (identifier-syntax count))
(indirect-export current-count count)
(define count 0)
(define bump-count
(lambda ()
(set! count (+ count 1)))))
(import M)
(bump-count)
current-count ;=> 1
count ;=> \var{exception: unbound identifier count}
\endschemedisplay
An \scheme{indirect-export} form is not required to make \scheme{count}
visible for \scheme{bump-count}, since it is a procedure whose code
is contained within the module rather than a macro that might expand
into a reference to \scheme{count} somewhere outside the module.
It is often useful to use \scheme{indirect-export} in the output
of a macro that expands into another macro named \var{a} if
\var{a} expands into references to identifiers that might not
be directly exported, as illustrated by the alternative definition
of module \scheme{M} above.
\schemedisplay
(define-syntax define-counter
(syntax-rules ()
[(_ getter bumper init incr)
(begin
(define count init)
(define-syntax getter (identifier-syntax count))
(indirect-export getter count)
(define bumper
(lambda ()
(set! count (incr count)))))]))
(module M (bump-count current-count)
(define-counter current-count bump-count 0 add1))
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{implicit-exports}{\categorysyntax}{(implicit-exports #t)}
\formdef{implicit-exports}{\categorysyntax}{(implicit-exports #f)}
\returns unspecified
\listlibraries
\endentryheader
An \scheme{implicit-exports} form is a definition and can appear with other
definitions at the front of a \scheme{library} or \scheme{module}.
It is a syntax error for an \scheme{implicit-exports} form to appear in other
contexts, including at top level or among the definitions of a
top-level program or \scheme{lambda} body.
The \scheme{implicit-exports} form determines whether identifiers
not directly exported from a module or library are automatically
indirectly exported to the top level if any meta-binding (keyword, meta
definition, or property definition) is directly exported to top level
from the library or module.
The default for libraries is \scheme{#t}, to match the behavior required
by the Revised$^6$ Report, while the default for modules is \scheme{#f}.
The \scheme{implicit-exports} form is meaningful only within a library,
top-level module, or module enclosed within a library or top-level module.
It is allowed in a module enclosed within a \scheme{lambda}, \scheme{let},
or similar body, but ignored there because none of that module's bindings
can be exported to top level.
The advantage of \scheme{(implicit-exports #t)} is that indirect exports
need not be listed explicitly, which is convenient.
A disadvantage is that it often results in more bindings than necessary
being elevated to top level where they cannot be discarded as useless
by the optimizer.
For modules, another disadvantage is such bindings
cannot be proven immutable, which inhibits important optimizations such
as procedure inlining.
This can result in significantly lower run-time performance.
\section{Explicitly invoking libraries\label{SECTLIBRARYINVOCATION}}
%----------------------------------------------------------------------------
\noskipentryheader
\formdef{invoke-library}{\categoryprocedure}{(invoke-library \var{libref})}
\returns unspecified
\listlibraries
\endnoskipentryheader
\var{libref} must be an s-expression in the form of a library reference.
The syntax for library references is given in
Chapter~\ref{TSPL:CHPTLIBRARIES} of {\TSPLFOUR} and in the Revised$^6$
Report.
A library is implicitly invoked when or before some expression
outside the library (e.g., in another library or in a top-level
program) evaluates a reference to one of the library's exported
variables.
When the library is invoked, its body expressions (the right-hand-sides
of the library's variable definitions and its initialization
expressions) are evaluated.
Once invoked, the library is not invoked again within the same process,
unless it is first explicitly redefined or reloaded.
\scheme{invoke-library} explicitly invokes the library specified
by \var{libref} if it has not already been invoked or has since
been redefined or reloaded.
If the library has not yet been loaded, \scheme{invoke-library}
first loads the library via the process described in
Section~\ref{SECTUSELIBRARIES}.
\scheme{invoke-library} is typically only useful for libraries whose
body expressions have side effects.
It is useful to control when the side effects occur and to force
invocation of a library that has no exported variables.
Invoking a library does not force the compile-time code (macro
transformer expressions and meta definitions) to be loaded or
evaluated, nor does it cause the library's bindings to become
visible.
It is good practice to avoid externally visible side effects in
library bodies so the library can be used equally well at compile
time and run time.
When feasible, consider moving the side effects of a library body
to an initialization routine and adding a top-level program that
imports the library and calls the initialization routine.
With this structure, calls to \scheme{invoke-library} on the
library can be replaced by calls to
\index{\scheme{load-program}}\scheme{load-program} on the
top-level program.
\section{Library Parameters\label{SECTLIBRARYPARAMETERS}}
\index{\scheme{import}}%
The parameters described below control where \scheme{import} looks
when attempting to load a library, whether it compiles the libraries
it loads, and whether it displays tracking messages as it performs its
search.
%----------------------------------------------------------------------------
\entryheader
\formdef{library-directories}{\categorythreadparameter}{library-directories}
\formdef{library-extensions}{\categorythreadparameter}{library-extensions}
\listlibraries
\endentryheader
The parameter \scheme{library-directories} determines where the files
containing library source and object code are located in the file system,
and the parameter \scheme{library-extensions} determines the filename
extensions for the files holding the code, as described in
section~\ref{SECTUSELIBRARIES}.
The values of both parameters are lists of pairs of strings.
The first string in each \scheme{library-directories} pair identifies a
source-file root directory, and the second identifies the corresponding
object-file root directory.
Similarly, the first string in each \scheme{library-extensions} pair
identifies a source-file extension, and the second identifies the
corresponding object-file extension.
The full path of a library source or object file consists of the source or
object root followed by the components of the library name prefixed by
slashes, with the library extension added on the end.
For example, for root \scheme{/usr/lib/scheme}, library name
\scheme{(app lib1)}, and extension \scheme{.sls}, the full path is
\scheme{/usr/lib/scheme/app/lib1.sls}.
If the library name portion forms an absolute pathname, e.g.,
\scheme{~/.myappinit}, the \scheme{library-directories} parameter is
ignored and no prefix is added.
The initial values of these parameters are shown below.
\schemedisplay
(library-directories) ;=> (("." . "."))
(library-extensions) ;=> ((".chezscheme.sls" . ".chezscheme.so")
;== (".ss" . ".so")
;== (".sls" . ".so")
;== (".scm" . ".so")
;== (".sch" . ".so"))
\endschemedisplay
As a convenience, when either of these parameters is set, any element of
the list can be specified as a single source string, in which case the
object string is determined automatically.
For \scheme{library-directories}, the object string is the same as
the source string, effectively naming the
same directory as a source- and object-code root.
For \scheme{library-extensions}, the object string is the result of
removing the last (or only) extension from the string and appending
\scheme{".so"}.
The \scheme{library-directories} and \scheme{library-extensions}
parameters also accept as input strings in the format described
in Section~\ref{SECTUSESCRIPTING}
for the
\index{\scheme{--libdirs} command-line option}\scheme{--libdirs} and
\index{\scheme{--libexts} command-line option}\scheme{--libexts} command-line
options.
%----------------------------------------------------------------------------
\entryheader
\formdef{compile-imported-libraries}{\categorythreadparameter}{compile-imported-libraries}
\listlibraries
\endentryheader
When the value of this parameter is \scheme{#t}, \scheme{import}
automatically calls the value of the \scheme{compile-library-handler} parameter (which defaults
to a procedure that simply calls \scheme{compile-library}) on any imported library if
the object file is missing, older than the corresponding source file,
older than any source files included (via \index{\scheme{include}}\scheme{include}) when the
object file was created, or itself requires a library that has or must
be recompiled, as described in Section~\ref{SECTUSELIBRARIES}.
The default initial value of this parameter is \scheme{#f}.
It can be set to \scheme{#t} via the command-line option
\index{\scheme{--compile-imported-libraries} command-line option}\scheme{--compile-imported-libraries}.
When \scheme{import} compiles a library via this mechanism, it does not
also load the compiled library, because this would cause portions of
library to be reevaluated.
Because of this, run-time expressions in the file outside of a
\scheme{library} form will not be evaluated.
If such expressions are present and should be evaluated, the library
should be loaded explicitly.
%----------------------------------------------------------------------------
\entryheader
\formdef{import-notify}{\categorythreadparameter}{import-notify}
\listlibraries
\endentryheader
When the new parameter \scheme{import-notify} is set to a true value,
\scheme{import} displays messages to the console-output port as it
searches for the file containing each library it needs to load.
The default value of this parameter is \scheme{#f}.
%----------------------------------------------------------------------------
\entryheader
\formdef{library-search-handler}{\categorythreadparameter}{library-search-handler}
\listlibraries
\endentryheader
The value of parameter must be a procedure that follows the protocol described
below for \scheme{default-library-search-handler}, which is the default value
of this parameter.
The value of this parameter is invoked to locate the source or object code for
a library during \scheme{import}, \scheme{compile-whole-program}, or
\scheme{compile-whole-library}.
%----------------------------------------------------------------------------
\entryheader
\formdef{default-library-search-handler}{\categoryprocedure}{(default-library-search-handler \var{who} \var{library} \var{directories} \var{extensions})}
\returns see below
\listlibraries
\endentryheader
This procedure is the default value of the \scheme{library-search-handler},
which is
called to locate the source or object code for a library
during \scheme{import},
\scheme{compile-whole-program}, or \scheme{compile-whole-library}.
\var{who} is a symbol that provides context in \scheme{import-notify} messages.
\var{library} is the name of the desired library.
\var{directories} is a list of source and object directory pairs in
the form returned by \scheme{library-directories}.
\var{extensions} is a list of source and object extension pairs in the form
returned by \scheme{library-extensions}.
This procedure searches the specified directories until it finds a library source or
object file with one of the specified extensions.
If it finds the source file first, it constructs the corresponding
object file path and checks whether the file exists.
If it finds the object file first, the procedure looks for a corresponding
source file with one of the given source extensions in a source directory paired
with that object directory.
The procedure returns three values:
the file-system path of the library source file or \scheme{#f} if not found,
the file-system path of the corresponding object file, which may be \scheme{#f},
and a boolean that is true if the object file exists.
\section{Library Inspection\label{SECTLIBRARYINSPECTION}}
%----------------------------------------------------------------------------
\noskipentryheader
\formdef{library-list}{\categoryprocedure}{(library-list)}
\returns a list of the libraries currently defined
\listlibraries
\endnoskipentryheader
The set of libraries initially defined includes those listed in
Section~\ref{SECTBUILTINLIBRARIES} above.
%----------------------------------------------------------------------------
\entryheader
\formdef{library-version}{\categoryprocedure}{(library-version \var{libref})}
\returns the version of the specified library
\formdef{library-exports}{\categoryprocedure}{(library-exports \var{libref})}
\returns a list of the exports of the specified library
\formdef{library-requirements}{\categoryprocedure}{(library-requirements \var{libref})}
\returns a list of libraries required by the specified library
\formdef{library-requirements}{\categoryprocedure}{(library-requirements \var{libref} \var{options})}
\returns a list of libraries required by the specified library, filtered by \var{options}
\formdef{library-object-filename}{\categoryprocedure}{(library-object-filename \var{libref})}
\returns the name of the object file holding the specified library, if any
\listlibraries
\endentryheader
Information can be obtained only for built-in libraries or libraries
previously loaded into the system.
\var{libref} must be an s-expression in the form of a library reference.
The syntax for library references is given in
Chapter~\ref{TSPL:CHPTLIBRARIES} of {\TSPLFOUR} and in the Revised$^6$
Report.
The \scheme{library-version} return value is a list of numbers
(possibly empty) representing the library's version.
The list of exports returned by \scheme{library-exports} is a list of
symbols, each identifying one of the library's exports.
The order in which the elements appear is unspecified.
When the optional \var{options} argument is supplied, it must be
an enumeration set over the symbols constituting
valid library-requirements options, as described in the
\scheme{library-requirements-options} entry below.
It defaults to a set containing all of the options.
Each element of the list of libraries returned by
\scheme{library-requirements} is an s-expression form of a library
reference.
The library reference includes the actual version of the library that is
present in the system (if nonempty), even if a version was not specified
when it was imported.
The order in which the libraries appear in the list returned by
\scheme{library-requirements} is unspecified.
\scheme{library-object-filename} returns a string naming the object
file if the specified library was loaded from or compiled to an object
file.
Otherwise, it returns \scheme{#f}.
\schemedisplay
(with-output-to-file "A.ss"
(lambda ()
(pretty-print
'(library (A (1 2)) (export x z)
(import (rnrs))
(define x 'ex)
(define y 23)
(define-syntax z
(syntax-rules ()
[(_ e) (+ y e)])))))
'replace)
(with-output-to-file "B.ss"
(lambda ()
(pretty-print
'(library (B) (export x w)
(import (rnrs) (A))
(define w (cons (z 12) x)))))
'replace)
(compile-imported-libraries #t)
(import (B))
(library-exports '(A)) ;=> (x z) ; or (z x)
(library-exports '(A (1 2))) ;=> (x z) ; or (z x)
(library-exports '(B)) ;=> (x w) ; or (w x)
(library-version '(A)) ;=> (1 2)
(library-version '(B)) ;=> ()
(library-requirements '(A)) ;=> ((rnrs (6)))
(library-requirements '(B)) ;=> ((rnrs (6)) (A (1 2)))
(library-object-filename '(A)) ;=> "A.so"
(library-object-filename '(B)) ;=> "B.so"
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{library-requirements-options}{\categorysyntax}{(library-requirements-options \var{symbol} \dots)}
\returns a library-requirements-options enumeration set
\listlibraries
\endentryheader
\noindent
Library-requirements-options enumeration sets are passed to
\scheme{library-requirements} to determine the library requirements
to be listed. The available options are described below.
\begin{description}
\item[\scheme{import}:]
Include the libraries that must be imported when the specified library
is imported.
\item[\scheme{visit@visit}:]
Includes the libraries that must be visited when the specified library
is visited.
\item[\scheme{invoke@visit}:]
Include the libraries that must be invoked when the specified library
is visited.
\item[\scheme{invoke}:]
Includes the libraries that must be invoked when the specified library
is invoked.
\end{description}