ERR5RS:Libraries

From SchemePunks

Jump to: navigation, search

Contents

[edit] Introduction

To maximize sharing and reuse of code between ERR5RS and R6RS systems, which will be especially important in systems that implement both ERR5RS and R6RS, the ERR5RS library syntax needs to have a usefully nonempty intersection with the R6RS library syntax. Unless there are good reasons to add new library syntax beyond that provided by the R6RS, the ERR5RS library syntax should be a useful proper subset of the R6RS library syntax.

ERR5RS itself, as well as several popular SRFIs and projected future ERR5RS libraries, consist of both variable and macro bindings. ERR5RS will therefore allow syntactic bindings to be exported and imported. Since ERR5RS only mandates high level syntax-rules macros, the essential library facility does not need to make provision for meta imports and complicated phases, which matter only when low-level macros are used. As a result, the syntax and semantics of ERR5RS libraries is much simpler than that of R6RS libraries.

The R6RS allows version numbers as part of a <library name>, but that feature is so controversial that most authors of R6RS libraries are likely to avoid it. ERR5RS will therefore drop the version number feature.

[edit] Syntax of ERR5RS programs and libraries

A portable ERR5RS program satisfies the following proposed extension of the R5RS program syntax of R5RS paragraph (7.1.6). The syntax refers to the result of macro expansion, which is defined below.

<program>               --> <command or definition>*
<command or definition> --> <command>
                          | <definition>
                          | <syntax definition>
                          | <library definition>
                          | <import form>
<import form>           --> (import <import spec>*)
<library definition>    --> (library <library name>
                              (export <export spec>*)
                              (import <import spec>*)
                              <library body>)
<library name>          --> (<identifier> <identifier>*)
<export spec>           --> <identifier> 
<import spec>           --> <import set>
<import set>            --> <library name>
                          | (only   <import set> <identifier>*)
                          | (except <import set> <identifier>*)
                          | (prefix <import set> <identifier>*)
                          | (rename <import set> (<identifier> <identifier>)*)
<library body>          --> <definition or syntax definition>* <command>*
<definition or syntax definition>
                        --> <definition>
                          | <syntax definition>

A <library definition> defines the library by associating the <library name> to a representation of the library, but does not evaluate the definitions or commands in the library.

In a portable program, a definition of a library must follow the definition of all libraries that it imports. Implementations must provide the essential and available optional err5rs standard libraries as predefined. Definitions of these must not appear in portable err5rs programs. Redefining a <library name> invalidates the definitions of any previously defined libraries importing the previous definition of <library name>.

Any variable or keyword imported into a library or bound in a library body using a library level define or define-syntax, can be exported from a library.

No identifier bindings are visible within a <library body> except for those explicitly imported into the library or defined within the library. It is an error to import a (non-standard) library for which a definition does not precede the <import from>. In a portable program, no identifier bindings are assumed visible within the top level <command or definition>* sequence of a <program> except for the default meanings of library, import, only, except, prefix, rename, bindings explicitly imported via a preceding <import form>, or bindings defined in the <command or definition>* sequence.

The only, except, prefix and rename keywords allow a subset of a library's bindings to be imported with possible renaming of identifiers. Their meaning is as described in R6RS.

In a portable library, no identifier is imported with more than one binding, both defined and imported, or defined more than once. In addition, no library or top-level macro use precedes its definition, and no reference to a top-level imported identifier precedes the first import form that imports the identifier.

Redefinitions are allowed at the top level as in the r5rs. In addition, an imported identifier may be shadowed by a subsequent top level definition, syntax definition, or import without affecting its binding in the exporting library, and a top level definition or syntax definition may be shadowed by a subsequent import. The semantics of such shadowing is described below. It is an error for a single top level <import form> to import more than one binding for the same identifier.

Exported and imported bindings are immutable in the following sense: No library binding may occur in the <variable> position of a set! expression outside its defining library, not even in code produced by an exported macro. No binding that occurs in the <variable> position of a set! expression may be exported from a library or occur outside the binding's defining library as a reference in code produced by an exported macro.

[edit] Import levels

The keyword bindings syntax-rules, ... and _ exported by the (err5rs) library may only be used in <transformer spec>s (see R5RS 7.1: Formal syntax) and are the only bindings that may be used in that context. This remains true if these keywords are indirectly imported via another library that imports and re-exports them. No other variable or syntactic binding imported using the above import syntax may be used in a <transformer spec>.

[edit] Library and program semantics

An ERR5RS program may be assigned a meaning by transforming it to a program in the sense of R5RS (section 5: Program structure) as follows:

  • Ignore the library definitions.
  • Consider each top level <import form> as equivalent to the top level sequence obtained by concatenating the unexpanded <library body>s of all the libraries listed in the <import form>, and all the libraries on which they depend, in an order consistent with their dependencies, using each <library body> once. All non-imported identifiers in each <library body> are effectively renamed to avoid conflicts with identifiers in other libraries or in the top level, and all imported identifiers in each library, and imported identifiers subsequent to the import form in the top level, are effectively renamed to refer to their bindings in their defining libraries.
  • Any top-level redefinition of an imported identifier causes references to the identifier in and subsequent to the definition to refer to the top-level binding, not the imported binding (see example).

For example, the ERR5RS program

   (library (foo)
     (export x)
     (import (err5rs))
     (define x 1))
   
   (library (bar)
     (export m)
     (import (err5rs) (foo))
     (define-syntax m
       (syntax-rules ()
         ((m z) (+ x y z))))
     (define y 2))
   
   (import (foo) (bar))
   (define u 3)
   (m u)             ==> 6  
   (define m 1)
   m                 ==> 1

is equivalent to the R5RS program

   (define foo_x 1)
   (define-syntax bar_m
     (syntax-rules ()
       ((bar_m bar_z) (+ foo_x bar_y bar_z))))
   (define bar_y 2)
   (define top_u 3)
   (bar_m top_u)     ==> 6
   (define top_m 1)
   top-m             ==> 1

Thus, evaluating an <import form> is equivalent to evaluating the top level sequence of <command or definition>s constructed by concatenating the library bodies as described.

[edit] Expansion process

Since the meaning of an ERR5RS program is defined in terms of an equivalent R5RS program, the expansion process of an ERR5RS program must be consistent with evaluating from left to right, as described in R5RS (section 5: Program structure), the equivalent R5RS sequence of definitions, syntax definitions, and commands. This R5RS sequence includes the spliced <library body>s, whose expansion is therefore also determined by the above transformation.

Given this semantics, an ERR5RS program, like an R5RS program, but unlike some R6RS <top-level program>s, may either be entered interactively in a running Scheme system where expansion is interleaved with evaluation, or be expanded fully before starting evaluation.

Since portable ERR5RS libraries contain no macro uses preceding their definitions, the meaning of a portable ERR5RS library remains the same under the R6RS expansion semantics. Note that R6RS libraries allow certain macro uses to precede their definitions. Such R6RS libraries are not valid in ERR5RS.

In a portable program, no macro use may introduce a <library definition>.

[edit] Dynamic loading of libraries

The library (err5rs load) exports the procedure load.

A library definition in a file may be evaluated in an interactive top-level environment by using the load procedure. ERR5RS extends the description of the load procedure in R5RS section 6.6.4 (System interface) by the following addition, indicated in italics: The load procedure reads any library definitions, import forms, expressions and definitions from the file and evaluates them sequentially.

[edit] Issues

  • The load procedure should not be used for including libraries in compiled programs. How to do so is unspecified, but see ERR5RS:Libraries and Compilation and also the discussions of the proposed (err5rs include) library.
  • Some issues to consider:
  • The need to specify, when a library is imported by more than one top-level IMPORT form, whether the library definitions and commands are evaluated once per program or once for every IMPORT form. Right now it is once per IMPORT form.
  • Expanding a library more than once breaks records because each expansion yields a new record type. User:kend
(library (rec-lib)
   (export  make-r r? r-val)
   (import (rnrs base)
           (err5rs records syntactic))

 (define-record-type r make-r r? val))
(load "rec-lib.sls")
(import (rec-lib))
(define r1 (make-r 'one))
(r? r1) ;==> #t

(load "rec-lib.sls")
;; equivalent to a 2nd expansion
(import (rec-lib))
(r? r1)  ;==> #f
  • Right now the above semantics implicitly specifies a behaviour for the following. Does this constrain implementations too much? Maybe we should make it undefined:
When shadowing a previously defined or imported identifier, a meaning is given unevaluated top-level references to the identifier preceding the shadowing.
Example of current semantics:
  (define x 1)
  (library (foo)
    (export x)
    (import (err5rs))
    (define x 2))
  (define (f) x)
  (import (foo))
  (define (g) x)
  x    ==> 2
  (f)  ==> 1   (*)
  (g)  ==> 2
  • Right now the following is probably not well-defined by text:
When shadowing a previously defined or imported identifier, what happens to references to the identifier in top-level macro templates preceding the shadowing?
Example:
  (define x 1)
  (library (foo)
    (export x)
    (import (err5rs))
    (define x 2))
  (define-syntax f
    (syntax-rules ()
      ((f) x)))
  (import (foo))
  (define-syntax g
    (syntax-rules ()
      ((g) x)))
  x    ==> 2
  (f)  ==> 2     (**) This is what my reference implementation gives 
  (g)  ==> 2
It would be possible to make the first example (*) behave like the second (**) by specifying that variable binding be copied on import to their top-level names. That may be specifying too much.
Personal tools