Chapter 24

package.ss: Local-Definition Scope Control

The package form provides fine-grained control over binding visibility. A package is an expansion-time entity only; it has no run-time identity.The package and open constructs correspond to module and import in Chez Scheme. The package* and open* constructs correspond to structures in Standard ML (without types).

(package name (export ···) body-expr-or-defn ···1)      SYNTAX

(package name all-defined body-expr-or-defn ···1)      SYNTAX

Defines name (in any definition context) to a compile-time package description, much in the way that (define-syntax a (syntax-rules ...)) binds a to a syntax expander, or (define-struct a ()) binds a to a compile-time structure type description.

Each export must be an identifier that is defined within the package body. The all-defined variant is shorthand for listing all identifiers that are defined in the package body.

Although package does not introduce a new binding scope, it hides all of the definitions in its body from definitions and expressions that are outside the package. The exported definitions become visible only when the package is opened with forms such as open.

Each body-expr-or-defn can be a definition or expression. Each defined identifier is visible in the entire package body, except definitions introduced by define*, define*-syntax, define*-values, define*-syntaxes, open*, package*, or define*-dot. The * forms expose identifiers to expressions and definitions that appear later in the package body, only, much like the sequential binding of let*. As with let*, an identifier can be defined multiple times within the package using * forms; if such an identifier is exported, the export corresponds to the last definition. For any other form of definition, the identifiers that it defines must be defined only once within the package.

When used in an internal-definition context (see section 2.8.5 in PLT MzScheme: Language Manual), name is immediately available for use with other forms, such as open, in the same internal-definition sequence.

For example, see open, below.

(package* name (export ···) body-expr-or-defn ···1)      SYNTAX

(package* name all-defined body-expr-or-defn ···1)      SYNTAX

Like package, but within a package body, the package name is visible only to later definitions and expressions.

(open name ···1)      SYNTAX

If a single name is provided, it must be defined as a package, and the package's exports are exposed in the definition context of the open declaration.

The open form acts like a definition form, in that it introduces bindings in a definition context, and such bindings can be exported from a package (even using all-defined). More precisely, however, open exposes bindings hidden by a package, rather than introducing identifiers. This exposure overrides any identifier that would shadow the binding (were it not hidden by the package in the first place).

If multiple names are provided, the first name must correspond to a defined package, the second must correspond to a package exported from the first, and so on. Only the package corresponding to the last name is opened into the open's definition context.

Examples:

(package p (f)
  (define (f a) (+ a x))
  (define x 1))
(f 0) ; => error: reference to undefined identifier f
(let ([p 5])
  (open p) ...)  ; => error: p is not a package name
(open p)
(f 0) ; => 1

(let ([f (lambda (x) x)])
  (open p)
  (f 0)) ; => 1

(let ([x 2])
  (open p)
  (f 0)) ; => 1

(package p (p2)
  (package p2 (f)
    (define (f a) (- a x)))
  (define x 2))
(open p p2)
(f 3) ; => 1

(package p (p2)
  (package p2 (f)
    (define (f a) (- a x)))
  (define x 2))
(open p p2)
(f 3) ; => 1

(package p1 (x f1 p2 p3)
  (define x 1)
  (define (f1) x)
  (package p2 (x f2)
    (define x 2)
    (define (f2) x))
  (package p3 (f3)
    (open p2)
    (define (f3) x)))
(open p1)
x ; => 1
(f1) ; => 1
(open p2)
x ; => 2
(f2) ; => 2
(open p3)
(f3) ; => 2
(open p1)
x ; => 1

(define-syntax package2
  (syntax-rules ()
   [(_ name id def)
    (package name (id foo)
      def
      (define foo 3))]))
(let ()
  (package2 p foo (define foo 1))
  (open p)
  foo) ; => 1
(let ()
  (package2 p bar (define bar 1))
  (open p)
  foo) ; => error:  reference to undefined identifier foo

(define-syntax open2
  (syntax-rules ()
   [(_ name) (open name)]))
(let ()
  (package p (x) (define x 1))
  (open2 p)
  x) ; => 1

(define-syntax package3
  (syntax-rules ()
   [(_ name id)
    (package name (id foo)
      (define (id) foo)
      (define foo 3))]))
(let ([foo 17])
  (package3 p f)
  (open p)
  (+ foo (f))) ; => 20

(open* name ···1)      SYNTAX

Like open, but within a package, the opened package's exports are exposed only to later definitions and expressions.

(dot name ···1export)      SYNTAX

Equivalent to (let () (open name ···1) export) when export is exported from the package selected by name ···1.

Example:

(package p (x)
  (define x 1))
(+ 2 (dot p x)) ; => 3

(define-dot variable name ···1)      SYNTAX

Defines variable as an alias for the package export selected by name ···1. The export can correspond to a nested package, in which case the alias is available for immediate use in forms like open or define-dot.

(define*-dot variable name ···1)      SYNTAX

Like define-dot, but within a package, the alias applies only to later definitions and expressions.

(rename-potential-package old-name new-name)      SYNTAX

Introduces old-name as an alias for new-name.

Although make-rename-transformer (see section 12.6 in PLT MzScheme: Language Manual) can be used to create an alias for a package name, only an alias created by rename-potential-package, define-dot, or define*-dot is available for immediate use by forms such as open.

(define* variable expr)      SYNTAX

(define* (header . formals) expr ···1)      SYNTAX

(define*-syntax variable expr)      SYNTAX

(define*-syntax (header . formals) expr ···1)      SYNTAX

(define*-values (variable ···) expr)      SYNTAX

(define*-syntaxes (variable ···) expr)      SYNTAX

(rename*-potential-package old-name new-name)      SYNTAX

Like define, etc., but when used in a package, they define identifiers that are visible only to later definitions and expressions.

(package/derived expr name (export ···) body-expr-or-defn ···1)      SYNTAX

(package/derived expr name all-defined body-expr-or-defn ···1)      SYNTAX

Like package, but syntax errors (such as duplicate definitions) are reported as originating from expr.

This form is useful for writing macros that expand to package and rely on the syntax checks of the package transformer, but where syntax errors should be reported in terms of the source expression or declaration.

(open/derived expr orig-name name ···1)      SYNTAX

(open*/derived expr orig-name name ···1)      SYNTAX

Like open and open*, but syntax errors (such as duplicate definitions) are reported as originating from expr. Furthermore, if name is not a package name, the error message reports that orig-name is not defined as a package.