SRFI 286: Controlling Library Availability

by Sergei Egorov

Status

This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-286@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

Abstract

R7RS Scheme allows programs to query library presence at macro-expansion time using
(cond-expand ((library ⟨library name ⟩) ...)). However, when an implementation ships a shared library tree across interpreters built with different feature configurations, or when third-party libraries depend on conditionally present implementation features, standardizing library presence detection becomes problematic. This proposal specifies library mask files (mask.slm), a declarative mechanism placed in library root directories to dynamically mask unavailable libraries based on the current feature set without requiring physical file removal or upfront parsing of library contents.

Rationale

R7RS cond-expand feature requirements may take the form (library ⟨library name ⟩).
According to R7RS:

"Each implementation maintains a list of feature identifiers which are present, as well as a list of libraries which can be imported. The value of a ⟨feature requirement ⟩ is determined by replacing each ⟨feature identifier ⟩ and (library ⟨library name ⟩) on the implementation’s lists with #t, and all other feature identifiers and library names with #f, then evaluating the resulting expression as a Scheme boolean expression under the normal interpretation of and, or, and not."

Implementors frequently desire to serve the same set of libraries (conventionally structured as a directory tree with .sld files as leaves) to accompany an interpreter that may be built to support a variety of features, such as full-unicode or exact-complex.

Some libraries are only usable if certain features are present in the build. While a library can use internal cond-expand forms to adapt its exports or provide polyfills, in many cases this is impossible because the underlying data types or runtime support are entirely missing. For instance, SRFI 160 ("Homogeneous numeric vector libraries") notes:

"A Scheme system that conforms to this SRFI does not have to support all of these homogeneous vector datatypes. However, a Scheme system must support float vectors if it supports Scheme inexact reals (of any precision). A Scheme system must support complex vectors if it supports Scheme inexact complex numbers (of any precision). Finally, a Scheme system must support a particular integer vector datatype if the system's exact integer datatype contains all the values that can be stored in such an integer vector. Thus a Scheme system with bignum support must implement all the integer vector datatypes, but a Scheme system might only support s8vectors, u8vectors, s16vectors and u16vectors if it only supports integers in the range
-229 to 229-1..."

Current approaches to handling feature-dependent library availability suffer from notable trade-offs:

  1. Deleting .sld files during installation: If feature support is fixed per build, unsupported .sld files can be deleted during installation. However, this precludes shipping multiple interpreters with different feature sets alongside a shared library tree, and complicates manual or package-managed installations.
  2. Leaving unsupported exports unbound: Libraries can be made available unconditionally, leaving missing functionality unbound. While easy to implement, this delays problem discovery until runtime and prevents standard (cond-expand ((library ...) ...) ...) checks from functioning correctly.
  3. Wrapping define-library inside internal cond-expand forms: Libraries can wrap their entire definition in a cond-expand. The drawback is that library availability discovery requires pre-loading or reading and parsing .sld files upfront, which severely degrades expansion and startup performance.

This proposal provides a middle ground: Library Mask Files. A single lightweight mask file located at the root of a library search directory allows implementations to conditionally hide whole sets of unavailable libraries based on the current run's feature set.

Specification

1. Mask File Location and Processing

A library mask file is a file named mask.slm. It may optionally be present at the root of any directory in the implementation's library search path.

When an interpreter loads or adds a directory to its library search path, it checks for the presence of mask.slm in that directory. If present, the file is read and evaluated against the implementation's currently active feature set.

The result of processing mask.slm is a list of library patterns associated with that specific search path directory. When searching for a library name (⟨name part ⟩ ...) in that directory:

2. Syntax of mask.slm

The contents of a mask.slm file should consist of a single cond-expand form.

To prevent circular dependencies and recursive lookup loops during library resolution, the feature requirements within mask.slm's cond-expand clauses are strictly limited to testing feature identifiers, boolean combinations (and, or, not), or implementation-defined non-library requirements. Testing library presence via (library ⟨library name ⟩) inside mask.slm is an error.

The right-hand side of a cond-expand clause in mask.slm should either be empty, contain a single (hide-libraries ⟨pattern ⟩ ...) form, or contain a nested cond-expand of the form being described. If selected, an empty clause or a clause containing (hide-libraries) with no patterns indicates that no libraries in this tree are masked.

As with standard cond-expand, clauses are evaluated sequentially, and only the first clause whose feature requirement is satisfied is selected; all subsequent clauses are ignored.

3. Pattern Syntax and Matching Rules

Patterns are S-expressions matching the following grammar:

⟨pattern ⟩ ⟶ * | ( ⟨pattern-sequence ⟩ ) | ( ⟨pattern-sequence ⟩ . * ) ⟨pattern-sequence ⟩ ⟶ ⟨empty ⟩ | ⟨pattern-segment ⟩ ⟨pattern-sequence ⟩ ⟨pattern-segment ⟩ ⟶ * | ⟨integer ⟩ | ⟨symbol ⟩

Matching is performed against a candidate library name represented as a list of symbols and/or exact integers (e.g., (srfi 160 s32)):

Examples

Example 1: Numeric Vector Libraries (SRFI 160)

Consider a Scheme implementation that can be invoked either in a lightweight fixnum+flonum mode or in a full numeric tower mode. In the lightweight mode, 32-bit and 64-bit integer vectors and complex vectors are unavailable.

Place the following mask.slm at the root of the library directory:

;; Path: /usr/local/share/fantastic-scheme/lib/mask.slm

(cond-expand
  ((not full-numeric-tower)
   (hide-libraries
     (srfi 160 s32)
     (srfi 160 u32)
     (srfi 160 s64)
     (srfi 160 u64)
     (srfi 160 c64)
     (srfi 160 c128)))
  (else))

Example 2: Wildcards and Tail Patterns

If a platform lacks thread support or native C-FFI, entire sub-trees of libraries can be masked cleanly using pattern matching:

(cond-expand
  ((not threads)
   (hide-libraries
     (concurrent . *)       ; Masks (concurrent sync), (concurrent pool), etc.
     (* thread)))           ; Masks (foo thread), (bar thread), etc.
  (else))

Implementation

A complete portable implementation is impossible. A sample base Scheme implementation of the pattern matcher and mask.slm evaluator can be found at srfi-286.scm.

Acknowledgments

Thanks to the Scheme community and SRFI authors for ongoing work on module and portability standards.

© 2026 Sergei Egorov.

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 (including the next paragraph) 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: Arthur A. Gleckler