21 Libraries
Libraries provide a way to organize klisp code with a clean & clear interface to the rest of the program. They are first class objects (as all manipulable entities in Kernel, and according to the guidelines in the Kernel report). A library has list of exported symbols and values for those symbols (generally combiners but may be any object). The library type is encapsulated.
In addition there’s a mechanism to refer to library objects by name (where a name is actually a unique list of symbols and numbers), with the ability to register new libraries, get the library object from a name, unregister libraries, etc.
These two ways of working with libraries conform the low level API to libraries and are orthogonal to each other. They are provided to allow klisp programs to construct their own interface to library definition and use, and to respect the guidelines and spirit of the Kernel Report.
There’s also a higher level API that is a combination of the lower level APIs and behaves more like traditional module systems. This is probably what most klisp programs will use. This API consist of the $provide-library!
operative to define (and register) new libraries and the $import-library!
operative to extract bindings out of existing libraries. This allows various forms of controlling which bindings are extracted from the libraries and allows various forms of renaming. It can be used both in the body of libraries or in the top level.
No association is made by klisp between source files and libraries. For a possible mechanism to handle this see require
in the ports module. Also no special meaning is assigned to the library names. This could be used by an even higher level API, for example to map to version numbers and/or to the underlying filesystem.
SOURCE NOTE: This is mostly an adaptation from the r7rs draft of scheme. There’s no mention of libraries in the Kernel Report, but they were deemed important for klisp in order to share and distribute code with a common interface. A number of adaptations were made to the scheme version to sit more comfortably with the Kernel way of doing things (first class status, exposing of the library register, etc).
The primitive type predicate for type library.
library?
returns true iff all the objects inobjects
are of type library.NOTE: This is part of the low level API to libraries. It won’t be needed to writers or users of libraries that stick to the higher level API (i.e.
$provide-library!
&$import-library!
).
bindings
should be an acyclic list of(symbol . value)
pairs. Each symbol may only occur once in the list.Constructs a new library object with the passed
bindings
as exported objects.NOTE: This is part of the low level API to libraries, writers of libraries are encouraged to use
$provide-library!
to define their libraries.
— Applicative: get-library-environment (get-library-environment library)
get-library-export-list
returns the list of symbols exported from the passed library.get-library-environment
returns a fresh empty environment whose parent has all exported symbols of the library bound to the exported objects.NOTE: This is part of the low level API to libraries, users of libraries are encouraged to use
$import-library!
to get bindings out of libraries (and into the current environment).
— Operative: $get-registered-library ($get-registered-library name)
— Operative: $register-library! ($register-library! name library)
— Operative: $unregister-library! ($unregister-library! name)
name
should a an acyclic list of symbols and exact non-negative integers. Two registered libraries can’t have the same name (in the sense ofequal?
).These operatives control the library registry that maps names to libraries.
Predicate
$registered-library?
returns true iff a library namedname
is already registered.get-registered-library
returns the library object associated withname
(or throws an error if no library namedname
is registered).$register-library!
registerslibrary
with namename
(throws an error ifname
is already registered.$unregister-library!
removes the library registered asname
from the library register (throws an error if no such library was registered).NOTE: This is part of the low level API to libraries, users & writers of libraries are encouraged to use
$provide-library!
to create & register new libraries.
— Operative: $import-library! ($import-library! . imports)
name
should be as forregister-library!
and not already registered.exports
should be a list of(#:export <export-spec> ...)
. Where<export spec>
is either:
symbol
(#:rename internal-symbol external-symbol)
A lone symbol has the same semantics as the pair with that symbol in both internal and external positions. No symbol can appear more than once as external.
body
should be an acyclic list of expressions.imports
should be a list like(<import-spec> ...)
where<import-spec>
is either
<name>
(#:only <import-spec> symbol ...)
(#:except <import-spec> symbol ...)
(#:prefix <import-spec> symbol)
(#:rename <import-spec> (orig-symbol new-symbol) ...)
These two operatives conform the higher level API for klisp libraries. They are what most users of klisp (both writers and users of libraries) will use.
Operative
$provide-library!
creates and register a library with namename
and exported symbols obtained from theexports
list and values prepared by thebody
. First a child of the dynamic environment is created. Then,body
is evaluated sequentially as if by$sequence
in that environment. Next a new library is created with a list of exported bindings that use the external symbols inexports
as names and the values bound by the corresponding internal symbols in the created environment, as values. If a lone symbol is used inexports
it is used both as internal and external symbol for that binding. Lastly, the new library object is registered asname
. This mechanism more or less follows the idea of operative$provide!
from the Kernel Report, but it also allows for renaming.Operative
$import-library!
imports to the current environment any combination of bindings from any number of named libraries, while allowing renaming of said bindings. It can be used in any context, as any other Kernel expression.$import-library!
looks for the named libraries and then extracts & renames the specified bindings according to each<import-spec>
and defines them (in the sense of$define!
and$set!
) in the current dynamic environment. The set of bindings to import are generated in a recursive manner. This allows a great deal of control of the imported bindings and their names. The semantics for the set of bindings generated by the various<import-spec>
s are as follows:
<name>
: All bindings from libraryname
.(#:only <import-spec> symbol ...)
: Only the named bindings from the set of bindings in<import-spec>
.(#:except <import-spec> symbol ...)
: All bindings from the set in<import-spec>
except those named in the list.(#:prefix <import-spec> symbol)
: All bindings from the set in<import-spec>
but renamed by prefixing each one with the specified prefixsymbol
.(#:rename <import-spec> (orig-symbol new-symbol) ...)
: All bindings from the set in<import-spec>
but renaming allorig-symbol
to the correspondingnew-symbol
.If two values are tried to be imported with the same name, they are checked for
eq?
-ness, if they are deemedeq?
to each other they are imported, otherwise$import-library!
throws an error. This helps catch name collisions while allowing to reexport bindings from used libraries without conflict.