#
Chapter 49
`unit.ss`: Core Units

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

MzScheme's units are used to organize a program into separately compilable and reusable components. A unit resembles a procedure in that both are first-class values that are used for abstraction. While procedures abstract over values in expressions, units abstract over names in collections of definitions. Just as a procedure is invoked to evaluate its expressions given actual arguments for its formal parameters, a unit is invoked to evaluate its definitions given actual references for its imported variables. Unlike a procedure, however, a unit's imported variables can be partially linked with the exported variables of another unit prior to invocation. Linking merges multiple units together into a single compound unit. The compound unit itself imports variables that will be propagated to unresolved imported variables in the linked units, and re-exports some variables from the linked units for further linking.

In some ways, a unit resembles a module (see Chapter 5 in PLT MzScheme: Language Manual), but units and modules serve different purposes overall. A unit encapsulates a pluggable component -- code that relies, for example, on ``some function `f` from a source to be determined later.'' In contrast, if a module imports a function, the import is ``the function `f` provided by the specific module `m`.'' Moreover, a unit is a first-class value that can be multiply instantiated, each time with different imports, whereas a module's context is fixed. Finally, because a unit's interface is separate from its implementation, units naturally support mutually recursive references across unit boundaries, while module imports must be acyclic.

MzScheme supports two layers of units. The core unit system comprises the `unit`, `compound-unit`, and `invoke-unit` syntactic forms. These forms implement the basic mechanics of units for separate compilation and linking. While the semantics of units is most easily understood via the core forms, they are too verbose for specifying the interconnections between units in a large program. Therefore, a system of units with signatures is provided on top of the core forms, comprising the `define-signature`, `unit/sig`, `compound-unit/sig`, and `invoke-unit/sig` syntactic forms.

The core system is described in this chapter, and defined by the `unit.ss` library. The signature system is described in section 50, and defined by `unitsig.ss`. Details about mixing core and signed units are presented in section 50.9 (using procedures from `unitsig.ss`).

## 49.1 Creating Units

The `unit` form creates a unit:

    (unit
      (import variable ···)
      (export exportage ···)
      unit-body-expr
      ···)

    exportage is one of
      variable
      (internal-variable external-variable)

The `variable`s in the `import` clause are bound within the `unit-body-expr` expressions. The variables for `exportage`s in the `export` clause must be defined in the `unit-body-expr`s as described below; additional private variables can be defined as well. The imported and exported variables cannot occur on the left-hand side of an assignment (i.e., a `set!` expression).

The first `exportage` form exports the binding defined as `variable` in the unit body using the external name `variable`. The second form exports the binding defined as `internal-variable` using the external name `external-variable`. The external variables from an `export` clause must be distinct.

Each exported `variable` or `internal-variable` must be defined in a `define-values` expression as a `unit-body-expr`.11 All identifiers defined by the `unit-body-expr`s together with the `variable`s from the `import` clause must be distinct.

### Examples

The unit defined below imports and exports no variables. Each time it is invoked, it prints and returns the current time in seconds:12

    (define f1@
      (unit (import) (export)
       (define x (current-seconds))
       (display x)
       (newline)
       x))

The unit defined below is similar, except that it exports the variable `x` instead of returning the value:

    (define f2@
      (unit (import) (export x)
       (define x (current-seconds))
       (display x)
       (newline)))

The following units define two parts of an interactive phone book:

    (define database@
      (unit
        (import show-message)
        (export insert lookup)

        (define table (list))
        (define insert
          (lambda (name info)
            (set! table (cons (cons name info) table))))
        (define lookup
          (lambda (name)
            (let ([data (assoc name table)])
              (if data
                  (cdr data)
                  (show-message "info not found")))))
        insert))

    (define interface@
      (unit
        (import insert lookup make-window make-button)
        (export show-message)
        (define show-message
          (lambda (msg) ...))
        (define main-window
          ...)))

In this example, the `database@` unit implements the database-searching part of the program, and the `interface@` unit implements the graphical user interface. The `database@` unit exports `insert` and `lookup` procedures to be used by the graphical interface, while the `interface@` unit exports a `show-message` procedure to be used by the database (to handle errors). The `interface@` unit also imports variables that will be supplied by a platform-specific graphics toolbox.

## 49.2 Invoking Units

A unit is invoked using the `invoke-unit` form:

    (invoke-unit unit-expr import-expr ···)

The value of `unit-expr` must be a unit. For each of the unit's imported variables, the `invoke-unit` expression must contain an `import-expr`. The value of each `import-expr` is imported into the unit. More detailed information about linking is provided in the following section on compound units.

Invocation proceeds in two stages. First, invocation creates bindings for the unit's private, imported, and exported variables. All bindings are initialized to the undefined value. Second, invocation evaluates the unit's private definitions and expressions. The result of the last expression in the unit is the result of the `invoke-unit` expression. The unit's exported variable bindings are not accessible after the invocation.

### Examples

These examples use the definitions from the earlier unit examples in section 49.1.

The `f1@` unit is invoked with no imports:

    (invoke-unit f1@) ; => displays and returns the current time

Here is one way to invoke the `database@` unit:

    (invoke-unit database@ display)

This invocation links the imported variable `show-message` in `database@` to the standard Scheme ``display`` procedure, sets up an empty database, and creates the procedures `insert` and `lookup` tied to this particular database. Since the last expression in the `database@` unit is `insert`, the `invoke-unit` expression returns the `insert` procedure (without binding any top-level variables). The fact that `insert` and `lookup` are exported is irrelevant to the invocation; exports are only used for linking.

Invoking the `database@` unit directly in the above manner is actually useless. Although a program can insert information into the database, it cannot extract information since the `lookup` procedure is not accessible. The `database@` unit becomes useful when it is linked with another unit in a `compound-unit` expression.

`(`define-values/invoke-unit` ``(`export-id `···``)` unit-expr [prefix import-id `···`]`)` S Y N T A X

This form is similar to `invoke-unit`. However, instead of returning the value of the unit's initialization expression, `define-values/invoke-unit` expands to a `define-values` expression that binds each identifier `export-id` to the value of the corresponding variable exported by the unit. At run time, if the unit does not export all of the `export-id`s, the `exn:fail:unit` exception is raised.

If `prefix` is specified, it must be either `#f` or an identifier. If it is an identifier, the names defined by the expansion of `define-values/invoke-unit` are prefixed with ``prefix`:`.

Example:

    (define x 3)
    (define y 2)
    (define-values/invoke-unit (c)
      (unit (import a b) (export c)
        (define c (- a b)))
      ex
      x y)
    ex:c ; => 1

`(`namespace-variable-bind/invoke-unit` ``(`export-id `···``)` unit-expr [prefix import-id `···`]`)` S Y N T A X

This form is like `define-values/invoke-unit`, but the expansion is a sequence of calls to ``namespace-set-variable-value!`` instead of a `define-values` expression. Thus, when it is evaluated, a `namespace-variable-bind/invoke-unit` expression binds top-level variables in the current namespace.

## 49.3 Linking Units and Creating Compound Units

The `compound-unit` form links several units into one new compound unit. In the process, it matches imported variables in each sub-unit either with exported variables of other sub-units or with its own imported variables:

    (compound-unit
      (import variable ···)
      (link (tag (sub-unit-expr linkage ···)) ···)
      (export (tag exportage ···) ···))

    linkage is one of
      variable
      (tag variable)
      (tag variable ···)

    exportage is one of
      variable
      (internal-variable external-variable)

    tag is
      identifier

The three parts of a `compound-unit` expression have the following roles:

- The `import` clause imports variables into the compound unit. These imported variables are used as imports to the compound unit's sub-units.

- The `link` clause specifies how the compound unit is created from sub-units. A unique `tag` is associated with each sub-unit, which is specified using an arbitrary expression. Following the unit expression, each `linkage` specifies a variable using the `variable` form or the `(`tag` `variable`)` form. In the former case, the `variable` must occur in the `import` clause of the `compound-unit` expression; in the latter case, the `tag` must be defined in the same `compound-unit` expression. The `(`tag` `variable` `···`)` form is a shorthand for multiple adjacent clauses of the second form with the same `tag`.

- The `export` clause re-exports variables from the compound unit that were originally exported from the sub-units. The `tag` part of each `export` sub-clause specifies the sub-unit from which the re-exported variable is drawn. The `exportage`s specify the names of variables exported by the sub-unit to be re-exported.

  As in the `export` clause of the `unit` form, a re-exported variable can be renamed for external references using the `(`internal-variable` `external-variable`)` form. The `internal-variable` is used as the name exported by the sub-unit, and `external-variable` is the name visible outside the compound unit.

The evaluation of a `compound-unit` expression starts with the evaluation of the `link` clause's unit expressions (in sequence). For each sub-unit, the number of variables it imports must match the number of `linkage` specifications that are provided, and each `linkage` specification is matched to an imported variable by position. Each sub-unit must also export those variables that are specified by the `link` and `export` clauses. If, for any sub-unit, the number of imported variables does not agree with the number of linkages provided, the `exn:fail:unit` exception is raised. If an expected exported variable is missing from a sub-unit for linking to another sub-unit, the `exn:fail:unit` exception is raised. If an expected export variable is missing for re-export, the `exn:fail:unit` exception is raised.

The invocation of a compound unit proceeds in two phases to invoke the sub-units. In the first phase, the compound unit resolves the imported variables of sub-units with the bindings provided for the compound unit's imports and new bindings created for sub-unit exports. In the second phase, the internal definitions and expressions of the sub-units are evaluated sequentially according to the order of the sub-units in the `link` clause. The result of invoking a compound unit is the result from the invocation of the last sub-unit.

### Examples

These examples use the definitions from the earlier unit examples in section 49.1.

The following `compound-unit` expression creates a (probably useless) renaming wrapping around the unit bound to `f2@`:

    (define f3@
      (compound-unit
        (import)
        (link [A (f2@)])
        (export (A (x A:x)))))

The only difference between `f2@` and `f3@` is that `f2@` exports a variable named `x`, while `f3@` exports a variable named `A:x`.

The following example shows how the `database@` and `interface@` units are linked together with a graphical toolbox unit `Graphics` to produce a single, fully-linked compound unit for the interactive phone book program.

    (define program@
      (compound-unit
        (import)
        (link (GRAPHICS (graphics@))
              (DATABASE (database@ (INTERFACE show-message)))
              (INTERFACE (interface@ (DATABASE insert lookup)
                                     (GRAPHICS make-window make-button))))
        (export)))

This phone book program is executed with `(invoke-unit program@)`. If `(invoke-unit program@)` is evaluated a second time, then a new, independent database and window are created.

## 49.4 Unit Utilities

`(unit?``` `v``)` returns `#t` if `v` is a unit or `#f` otherwise.

11 The detection of unit definitions is the same as for internal definitions (see section 2.8.5 in PLT MzScheme: Language Manual). Thus, the `define` and `define-struct` forms can be used for definitions.

12 The ```@`'' in the variable name ```f1@`'' indicates (by convention) that its value is a unit.