Libraries

The design of the library system was a challenging process: Many existing Scheme implementations offer “module systems”, but they differ dramatically both in functionality and in the goals they address. The library system was designed with the primary requirement of allowing programmers to write, distribute, and evolve portable code. A secondary requirement was to be able to separately compile libraries in the sense that compiling a library requires only having compiled its dependencies. This entailed the following corollary requirements:

The library system does not address the following goals, which were considered during the design process:

This section discusses some aspects of the design of the library system that have been controversial:

7.1  Syntax

A library definition is a single form, rather than a sequence of forms where some forms are some kind of header and the remaining forms contain the actual code. It is not clear that a sequence of forms is more convenient than a single form for processing and generation. Both syntactic choices have technical merits and drawbacks. The single-form syntax chosen for R6RS has the advantage of being self-delimiting.

A difference between top-level programs and libraries is that a program contains only one top-level program but multiple libraries. Thus, delimiting the text for a library body will be a a common need (in streams of various kinds) that it is worth standardizing the delimiters; parentheses are the obvious choice.

7.2  Local import

Some Scheme implementations feature module systems that allow importing a module’s bindings into a local environment. While local import has several benefits, a drawback of local imports is that library dependencies are not readily apparent from a library header. This drawback seems significant enough to merit longer-term consideration. Since leaving out local import for now does not preclude it from being added later, and since the report already tackles so many library-related issues, the editors decided to omit local import from the report.

7.3  Local modules

Some Scheme implementations feature module systems that allow defining “local modules” in a local environment rather than only at the library level. Specifically, this enables syntactic abstraction over modules. Local modules are useful, but also complicate the scoping rules of the language: Whereas the library system only allows transporting bindings from one library top level to another, local modules allow transporting bindings into local scopes, with far-reaching consequences for the rules that may be used to determine scoping, particularly in the absence of syntax-case.

Moreover it is debatable whether a top-level library should be the same construct as a local module. The job of the library system is to organize the top-level namespace, and not to serve as a target for expansion of sophisticated macros. Of course, the broader roles and syntactic similarity of module and library suggest merging the concepts, but merging the concepts further broadens the role of each. Such generalization may seem intuitively right to Scheme programmers, but all attempts by the editors at such broadening led away from consensus rather than toward it.

7.4  Fixed import and export clauses

The import and export clauses of the library form are fixed—they cannot be the result of macro expansion. Just as with local import, this decision reflects the desire to make library dependencies readily apparent from the library header.

7.5  Instantiation and initialization

Opinions vary on how libraries should be instantiated and initialized during the expansion and execution of library bodies, whether library instances should be distinguished across phases, and whether levels should be declared so that they constrain identifier uses to particular phases. This report therefore leaves considerable latitude to implementations, while attempting to provide enough guarantees to make portable libraries feasible.

7.6  Immutable exports

The asymmetry in the prohibitions against assignments to explicitly and implicitly exported variables reflects the fact that the violation can be determined for implicitly exported variables only when the importing library is expanded.

7.7  Compound library names

Library names are compound. This differs from the treatment of identifiers in the rest of the language. Using compound names reflects experience across programming languages that a structured top-level name space is necessary to avoid collisions. Embedding a hierarchy within a single string or symbol is certainly possible. However, in Scheme, list data is the natural means for represent hierarchical structure, rather than encoding it in a string or symbol. The hierarchical structure makes it easy formulating policies for choosing unique names, or possible storage formats in a file system. See appendix on “Unique library names”. Consequently, despite the syntactic complexity of compound names, and despite the potential mishandling of the hierarchy by implementations, the editors chose the list representation.

7.8  Versioning

Libraries and import clauses optionally carry versioning information. This allows reflecting the development history of a library, but also significantly increases the complexity of the library system. Experience with module systems gathered in other languages as well as with shared libraries at the operating-system level consistently indicates that relying only on the name of a module for identification causes conflicts impossible to rectify in the absence of versioning information, and thus diminishes the opportunities for sharing code. Therefore, versioning is part of the library system.

7.9  Treatment of different versions

Libraries with the same name but different versions cannot coexist within the same program. This prevents combining libraries and programs that require conflicting versions of the same library. An alternative choice would have been to allow the co-existence of different versions of the same library. Based on experience with other shared-library systems (including Windows DLLs and Unix shared objects), the potential for confusion created by multiple library instances seems too great. Therefore, the reports recommends against multiple instances of a library.