#
Chapter 28
`package.ss`: Local-Definition Scope Control

To load: `(require (lib "package.ss"))`

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`)` S Y N T A X

`(`package` `name `all-defined` body-expr-or-defn `···`1`)` S Y N T A X

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`)` S Y N T A X

`(`package*` `name `all-defined` body-expr-or-defn `···`1`)` S Y N T A X

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

`(`open` `name `···`1`)` S Y N T A X

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 `name`s 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`)` S Y N T A X

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

`(`dot` `name `···`1export`)` S Y N T A X

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`)` S Y N T A X

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`)` S Y N T A X

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

`(`rename-potential-package` `old-name new-name`)` S Y N T A X

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`)` S Y N T A X

`(`define*` ``(`header` . `formals`)` expr `···`1`)` S Y N T A X

`(`define*-syntax` `variable expr`)` S Y N T A X

`(`define*-syntax` ``(`header` . `formals`)` expr `···`1`)` S Y N T A X

`(`define*-values` ``(`variable` `···`)` expr`)` S Y N T A X

`(`define*-syntaxes` ``(`variable` `···`)` expr`)` S Y N T A X

`(`rename*-potential-package` `old-name new-name`)` S Y N T A X

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`)` S Y N T A X

`(`package/derived` `expr name `all-defined` body-expr-or-defn `···`1`)` S Y N T A X

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`)` S Y N T A X

`(`open*/derived` `expr orig-name name `···`1`)` S Y N T A X

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.