ERR5RS:(err5rs include) Library

From SchemePunks

Jump to: navigation, search

Contents

[edit] Description

The (err5rs include) library provides a means to interpolate source files into source code. It exports the syntactic keyword include.

Syntax: (include file-name-specifier)

Syntax: (include file-name-prefix-specifier file-name-specifier)

File-name-specifier must be a literal string or a non-empty literal list of identifiers. File-name-prefix-specifier must be a literal string or a literal identifier.

The first include form is transformed to

 (include "" file-name-specifier)

The syntactic context of the include identifier in the transformation is the same as that of the include identifier in the original form.

The second include form looks for an existing file whose name is formed from file-name-prefix-specifier and file-name-specifier. If the file is found, the include form is transformed to

 (begin
   datum ...)

where the datum are read in order from the file. Each datum in the result of the transformation has the same syntactic context as the include identifier in the original form, with the effect that it behaves as if it were introduced into the code when include was introduced.

The file name is formed from file-name-prefix-specifier and file-name-specifier by attempting to convert them to strings and then, if the conversion is successful, concatenating these strings. The conversion of literal strings always succeeds and produces the same literal string. The conversion of non-strings is left to the implementation.

[edit] Examples

The examples assume that (err5rs include) has been imported appropriately.

If the file f.scm contains

 (define f (lambda (x) (g (* x x))))

and the file g.scm contains

 (define g (lambda (x) (+ x x)))

then the expression

 (let ()
   (include "f.scm")
   (include "g.scm")
   (f 5))

evaluates to 50.

On a POSIX system that follows the recommendations below, if the file /dir/a/b/c.scm contains

 (display "This is /dir/a/b/c.scm")
 (newline)

and if the environment variable DIR_PREFIX is set to /dir/, then

 (include DIR_PREFIX (a b c))

will print

 This is /dir/a/b/c.scm.

[edit] Rationale and Notes

[edit] The Case for Standardization

include is useful but cannot be implemented using only the required features of the ERR5RS. (It can be implemented using optional syntax-case macros.)

[edit] include

The name include is preferred over include-file as it reflects current practice and is in agreement with the procedure load (which is not named load-file).

Interpreting the arguments as a file name does not prejudice a possible future extension to interpret the argument to be an URI as the overlap between valid file names on common systems and URIs is small.

The arguments are required to be literal constants because allowing the user to evaluate arbitrary code during expansion would create difficulties for some implementations.

The behavior of include if the file does not exist or does not contain data is not specified. This follow the general practice of the R5RS in specifying the behavior of correct programs and leaving the behavior of incorrect programs to the implementation.

The two-argument version is useful for including files that might be located in different places on different systems. See, for example, the discussion on libraries below.

[edit] Recommendations for POSIX systems

On POSIX systems, it is recommended that identifier file name prefix specifiers be converted to an upper-case string and that this string be used as a key into the system environment. If an entry for the key exists, the corresponding value should be converted to a string and used in place of the identifier.

On POSIX systems, it is recommended that non-empty list file name specifiers be converted to strings by the following algorithm

 (lambda (symbol-list)
   (define (string-downcase string)
     (list->string (map char-downcase (string->list string))))
   (if (null? symbol-list)
       ".scm"
       (string-append (string-downcase (symbol->string (car symbol-list)))
                      (if (null? (cdr symbol-list))
                          ""
                          "/")
                      (symbol-list->file-name (cdr symbol-list)))))

[edit] Libraries

The include form is useful for finding and composing libraries defined in source files. For example, one might create files interface.scm and implementation.scm where interface.scm contains:

 (library (...)
   (export ...)
   (import ...)
   (include "/path/to/implementation.scm"))

and implementation.scm contains a library body that implements the library. One could make this library available using

 (include "/path/to/interface.scm")

However, when this library is installed in a different place, perhaps when the program is ported to a different system, all of the absolute paths have to be adjusted. This is inconvenient.

The two-argument include form can be used to solve this problem. For example, one can write interface.scm as

 (library (...)
   (export ...)
   (import ...)
   (include-library IMPLEMENTATION_PREFIX "implementation.scm"))

and then makes the library available using

 (include-library INTERFACE_PREFIX "interface.scm")

In this case, when the library is moved, only the definitions of INTERFACE_PREFIX and IMPLEMENTATION_PREFIX (according to the implementation) need to be adjusted.

[edit] include-relative

The "obvious" solution to the composition problem is to have include interpret relative file names with respect to the file into which they are included. Such an include-relative form was suggested by Per Bothner during the discussion of SRFI 59.

include-relative can be implemented by having the expander mark each included form with the vicinity of the file from which it was included or by mutating an expansion-time variable. Unfortunately, these implementation strategies present problems for some implementations and there does not seem to be a portable alternative.

[edit] Syntactic Context

The syntactic context of the interpolated forms is the taken from the include keyword. This makes it difficult to use include in a macro exported from a library. For example, suppose we define

 (library (foo)
    (export include-foo)
    (import (err5rs include))
    (define-syntax include-foo
      (syntax-rules ()
        ((_ file-name)
         (include "/foo/" file-name)))))

If this library is imported and include-foo is used, the interpolated forms will be interpreted in the syntactic context of the definition of include-foo, that is, in the context of the body of the library (foo), and not in the syntactic context of the use of include-foo. This may be surprising.

It is possible to define more complicated versions of include that do not suffer from this restriction, but the simpler version defined here is adequate for most common uses.

[edit] Issues

[edit] Reference Implementation

[edit] Acknowledgments

The first example is adapted from the draft R6RS.

[edit] References

SRFI 59: Vicinities