#
Chapter 50
`unitsig.ss`: Units with Signatures

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

The unit syntax presented in section 49 poses a serious notational problem: each variable that is imported or exported must be separately enumerated in many `import`, `export`, and `link` clauses. Consider the phone book program example from section 49.3: a realistic `graphics@` unit would contain many more procedures than `make-window` and `make-button`, and it would be unreasonable to enumerate the entire graphics toolbox in every client module. Future extensions to the graphics library are likely, and while the program must certainly be re-compiled to take advantage of the changes, the programmer should not be required to change the program text in every place that the graphics library is used.

This problem is solved by separating the specification of a unit's signature (or ``interface'') from its implementation. A unit signature is essentially a list of variable names. A signature can be used in an import clause, an export clause, a link clause, or an invocation expression to import or link a set of variables at once. Signatures clarify the connections between units, prevent mis-orderings in the specification of imported variables, and provide better error messages when an illegal linkage is specified.

Signatures are used to create units with signatures, a.k.a. signed units. Signatures and signed units are used together to create signed compound units. As in the core system, a signed compound unit is itself a signed unit.

Signed units are first-class values, just like their counterparts in the core system. A signature is not a value. However, signature information is bundled into each signed unit value so that signature-based checks can be performed at run time (when signed units are linked and invoked).

Along with its signature information, a signed unit includes a primitive unit from the core system that implements the signed unit. This underlying unit can be extracted for mixed-mode programs using both signed and unsigned units. More importantly, the semantics of signed units is the same as the semantics for regular units; the additional syntax only serves to specify signatures and to check signatures for linking.

## 50.1 Importing and Exporting with Signatures

The `unit/sig` form creates a signed unit in the same way that the `unit` form creates a unit in the core system. The only difference between these forms is that signatures are used to specify the imports and exports of a signed unit.

In the primitive `unit` form, the `import` clause only determines the number of variables that will be imported when the unit is linked; there are no explicitly declared connections between the import variables. In contrast, a `unit/sig` form's `import` clause does not specify individual variables; instead, it specifies the signatures of units that will provide its imported variables, and all of the variables in each signature are imported. The ordered collection of signatures used for importing in a signed unit is the signed unit's import signature.

Although the collection of variables to be exported from a `unit/sig` expression is specified by a signature rather than an immediate sequence of variables,13 variables are exported in a `unit/sig` form in the same way as in the `unit` form. The export signature of a signed unit is the collection of names exported by the unit.

Example:

    (define-signature arithmetic^ (add subtract multiply divide power))
    (define-signature calculus^ (integrate))
    (define-signature graphics^ (add-pixel remove-pixel))
    (define-signature gravity^ (go))
    (define gravity@
      (unit/sig gravity^ (import arithmetic^ calculus^ graphics^)
        (define go (lambda (start-pos) ... subtract ... add-pixel ...))))

In this program fragment, the signed unit `gravity@` imports a collection of arithmetic procedures, a collection of calculus procedures, and a collection of graphics procedures. The arithmetic collection will be provided through a signed unit that matches the `arithmetic^` (export) signature, while the graphics collection will be provided through a signed unit that matches the `graphics^` (export) signature. The `gravity@` signed unit itself has the export signature `gravity^`.

Suppose that the procedures in `graphics^` were named `add` and ``remove`` rather than `add-pixel` and `remove-pixel`. In this case, the `gravity@` unit cannot import both the `arithmetic^` and `graphics^` signatures as above, because the name `add` would be ambiguous in the unit body. To solve this naming problem, the imports of a signed unit can be distinguished by providing prefix tags:

    (define-signature graphics^ (add remove))
    (define gravity@
      (unit/sig gravity^ (import (a : arithmetic^) (c : calculus^) (g : graphics^))
        (define go (lambda (start-pos) ... a:subtract ... g:add ...))))

Details for the syntax of signatures are in section 50.2. The full `unit/sig` syntax is described in section 50.3.

## 50.2 Signatures

A `signature` is either a signature description or a bound signature identifier:

    (sig-element ···)
    signature-identifier

    sig-element is one of
      variable
      (struct base-identifier (field-identifier ···) omission ···)
      (open signature)
      (unit identifier : signature)

    omission is one of
      -selectors
      -setters
      (- variable)

Together, the element descriptions determine the set of elements that compose the signature:

- The simple `variable` form adds a variable name to the new signature.

- The `struct` form expands into the list of variable names generated by a `define-struct` expression with the given `base-identifier` and `field-identifier`s.

  The actual structure type can contain additional fields; if a field identifier is omitted, the corresponding selector and setter names are not added to the signature. Optional `omission` specifications can omit other kinds of names: `-selectors` omits all field selector variables. `-setters` omits all field setter variables, and `(`-` `variable`)` omits a specific generated `variable`.

  In a unit importing the signature, the `base-identifier` is also bound to expansion-time information about the structure type (see section 12.6.4 in PLT MzScheme: Language Manual). The expansion-time information records the descriptor, constructor, predicate, field accessor, and field mutator bindings from the signature. It also indicates that the accessor and mutator sets are potentially incomplete (so `match` works with the structure type, but `shared` does not), either because the signature omits fields, or because the structure type is derived from a base type (which cannot be declared in a signature, currently).

- The `open` form copies all of the elements of another signature into the new signature description.

- The `unit` form creates a sub-signature within the new signature. A signature that includes a `unit` clause corresponds to a signed compound unit that exports an embedded unit. (Embedded units are described in section 50.6 and section 50.7.)

The names of all elements in a signature must be distinct.14 Two signatures match when they contain the same element names, and when a name in both signatures is either a variable name in both signatures or a sub-signature name in both signatures such that the sub-signatures match. The order of elements within a signature is not important. A source signature satisfies a destination signature when the source signature has all of the elements of the destination signature, but the source signature may have additional elements.

The `define-signature` form binds a signature to an identifier:

    (define-signature signature-identifier signature)

The `let-signature` form binds a signature to an identifier within a body of expressions:

    (let-signature identifier signature body-expr ···1)

For various purposes, signatures must be flattened into a linear sequence of variables. The flattening operation is defined as follows:

- All variable name elements of the signature are included in the flattened signature.

- For each sub-signature element named `s`, the sub-signature is flattened, and then each variable name in the flattened sub-signature is prefixed with ``s`:` and included in the flattened signature.

## 50.3 Signed Units

The `unit/sig` form creates a signed unit:

    (unit/sig signature
      (import import-element ···)
      renames
      unit-body-expr
      ···)

    import-element is one of
      signature
      (identifier : signature)

    renames is either empty or
      (rename (internal-variable signature-variable) ···)

The `signature` immediately following `unit/sig` specifies the export signature of the signed unit. This signature cannot contain sub-signatures. Each element of the signature must have a corresponding variable definition in one of the `unit-body-expr`s, modulo the optional `rename` clause. If the `rename` clause is present, it maps `internal-variable`s defined in the `unit-body-expr`s to `signature-variable`s in the export signature.

The `import-element`s specify imports for the signed unit. The names bound within the `signed-unit-body-expr`s to imported bindings are constructed by flattening the signatures according to the algorithm in section 50.2:

- For each `import-element` using the `signature` form, the variables in the flattened signature are bound in the `signed-unit-body-expr`s.

- For each `import-element` using the `(`identifier` : `signature`)` form, the variables in the flattened signature are prefixed with ``identifier`:` and the prefixed variables are bound in the `signed-unit-body-expr`s.

## 50.4 Linking with Signatures

The `compound-unit/sig` form links signed units into a signed compound unit in the same way that the `compound-unit` form links primitive units. In the `compound-unit/sig` form, signatures are used for importing just as in `unit/sig` (except that all import signatures must have a tag), but the use of signatures for linking and exporting is more complex.

Within a `compound-unit/sig` expression, each unit to be linked is represented by a tag. Each tag is followed by a signature and an expression. A tag's expression evaluates (at link-time) to a signed unit for linking. The export signature of this unit must satisfy the tag's signature. ``Satisfy'' does not mean ``match exactly''; satisfaction requires that the unit exports at least the variables specified in the tag's signature, but the unit may actually export additional variables. Those additional variables are ignored for linking and are effectively hidden by the compound unit.

To specify the compound unit's linkage, an entire unit is provided (via its tag) for each import of each linked unit. The number of units provided by a linkage must match the number of signatures imported by the linked unit, and the tag signature for each provided unit must match (exactly) the corresponding imported signature.

The following example shows the linking of an arithmetic unit, a calculus unit, a graphics unit, and a gravity modeling unit:

    (define-signature arithmetic^ (add subtract multiply divide power))
    (define-signature calculus^ (integrate))
    (define-signature graphics^ (add-pixel remove-pixel))
    (define-signature gravity^ (go))
    (define arithmetic@ (unit/sig arithmetic^ (import) ...))
    (define calculus@ (unit/sig calculus^ (import arithmetic^) ...))
    (define graphics@ (unit/sig graphics^ (import) ...))
    (define gravity@ (unit/sig gravity^ (import arithmetic^ calculus^ graphics^) ...))
    (define model@
      (compound-unit/sig
        (import)
        (link (ARITHMETIC : arithmetic^ (arithmetic@))
              (CALCULUS : calculus^ (calculus@ ARITHMETIC))
              (GRAPHICS : graphics^ (graphics@))
              (GRAVITY : gravity^ (gravity@ ARITHMETIC CALCULUS GRAPHICS)))
        (export (var (GRAVITY go)))))

In the `compound-unit/sig` expression for `model@`, all link-time signature checks succeed since, for example, `arithmetic@` does indeed implement `arithmetic^` and `gravity@` does indeed import units with the `arithmetic^`, `calculus^`, and `graphics^` signatures.

The export signature of a signed compound unit is implicitly specified by the `export` clause. In the above example, the `model@` compound unit exports a `go` variable, so its export signature is the same as `gravity^`. More forms for exporting are described in section 50.6.

## 50.5 Restricting Signatures

As explained in section 50.4, the signature checking for a linkage requires that a provided signature exactly matches the corresponding import signature. At first glance, this requirement appears to be overly strict; it might seem that the provided signature need only satisfy the imported signature. The reason for requiring an exact match at linkages is that a `compound-unit/sig` expression is expanded into a `compound-unit` expression. Thus, the number and order of the variables used for linking must be fully known at compile time.

The exact-match requirement does not pose any obstacle as long as a unit is linked into only one other unit. In this case, the signature specified with the unit's tag can be contrived to match the importing signature. However, a single unit may need to be linked into different units, each of which may use different importing signatures. In this case, the tag's signature must be ``bigger'' than both of the uses, and a restricting signature is explicitly provided at each linkage. The tag must satisfy every restricting signature (this is a syntactic check), and each restricting signature must exactly match the importing signature (this is a run-time check).

In the example from section 50.4, both `calculus@` and `gravity@` import numerical procedures, so both import the `arithmetic^` signature. However, `calculus@` does not actually need the `power` procedure to implement `integrate`; therefore, `calculus@` could be as effectively implemented in the following way:

    (define-signature simple-arithmetic^ (add subtract multiply divide))
    (define calculus@ (unit/sig calculus^ (import simple-arithmetic^) ...))

Now, the old `compound-unit/sig` expression for `model@` no longer works. Although the old expression is still syntactically correct, link-time signature checking will discover that `calculus@` expects an import matching the signature `simple-arithmetic^` but it was provided a linkage with the signature `arithmetic^`. On the other hand, changing the signature associated with `ARITHMETIC` to `simple-arithmetic^` would cause a link-time error for the linkage to `gravity@`, since it imports the `arithmetic^` signature.

The solution is to restrict the signature of `ARITHMETIC` in the linkage for `CALCULUS`:

    (define model@
      (compound-unit/sig
        (import)
        (link (ARITHMETIC : arithmetic^ (arithmetic@))
              (CALCULUS : calculus^ (calculus@ (ARITHMETIC : simple-arithmetic^)))
              (GRAPHICS : graphics^ (graphics@))
              (GRAVITY : gravity^ (gravity@ ARITHMETIC CALCULUS GRAPHICS)))
        (export (var (GRAVITY go)))))

A syntactic check will ensure that `arithmetic^` satisfies `simple-arithmetic^` (i.e., `arithmetic^` contains at least the variables of `simple-arithmetic^`). Now, all link-time signature checks will succeed, as well.

## 50.6 Embedded Units

Signed compound units can re-export variables from linked units in the same way that core compound units can re-export variables. The difference in this case is that the collection of variables that are re-exported determines an export signature for the compound unit. Using certain export forms, such as the `open` form instead of the `var` form (see section 50.7), makes it easier to export a number of variables at once, but these are simply shorthand notations.

Signed compound units can also export entire units as well as variables. Such an exported unit is an embedded unit of the compound unit. Extending the example from section 50.5, the entire `gravity@` unit can be exported from `model@` using the `unit` export form:

    (define model@
      (compound-unit/sig
        (import)
        (link (ARITHMETIC : arithmetic^ (arithmetic@))
              (CALCULUS : calculus^ (calculus@ (ARITHMETIC : simple-arithmetic^)))
              (GRAPHICS : graphics^ (graphics@))
              (GRAVITY : gravity^ (gravity@ ARITHMETIC CALCULUS GRAPHICS)))
        (export (unit GRAVITY))))

The export signature of `model@` no longer matches `gravity^`. When a compound unit exports an embedded unit, the export signature of the compound unit has a sub-signature that corresponds to the full export signature of the embedded unit. The following signature, `model^`, is the export signature for the revised `model@`:

    (define-signature model^ ((unit GRAVITY : gravity^)))

The signature `model^` matches the (implicit) export signature of `model@` since it contains a sub-signature named `GRAVITY` -- matching the tag used to export the `gravity@` unit -- that matches the export signature of `gravity@`.

The export form `(unit GRAVITY)` does not export any variable other than `gravity@`'s `go`, but the ``unitness'' of `gravity@` is intact. The embedded `GRAVITY` unit is now available for linking when `model@` is linked to other units.

Example:

    (define tester@ (unit/sig () (import gravity^) (go 0)))
    (define test-program@
      (compound-unit/sig
         (import)
         (link (MODEL : model^ (model@))
               (TESTER : () (tester@ (MODEL GRAVITY))))
         (export)))

The embedded `GRAVITY` unit is linked as an import into the `tester@` unit by using the path `(MODEL GRAVITY)`.

## 50.7 Signed Compound Units

The `compound-unit/sig` form links multiple signed units into a new signed compound unit:

    (compound-unit/sig
      (import (tag : signature) ···)
      (link (tag : signature (expr linkage ···)) ···)
      (export export-element ···))

    linkage is
      unit-path

    unit-path is one of
      simple-unit-path
      (simple-unit-path : signature)

    simple-unit-path is one of
      tag
      (tag identifier ···)

    export-element is one of
      (var (simple-unit-path variable))
      (var (simple-unit-path variable) external-variable)
      (open unit-path)
      (unit unit-path)
      (unit unit-path variable)

    tag is
      identifier

The `import` clause is similar to the `import` clause of a `unit/sig` expression, except that all imported signatures must be given a `tag` identifier.

The `link` clause of a `compound-unit/sig` expression is different from the `link` clause of a `compound-unit` expression in two important aspects:

- Each sub-unit tag is followed by a `signature`. This signature corresponds to the export signature of the signed unit that will be associated with the tag.

- The linkage specification consists of references to entire signed units rather than to individual variables that are exported by units. A referencing `unit-path` has one of four forms:

  - The `tag` form references an imported unit or another sub-unit.

  - The `(`tag` : `signature`)` form references an imported unit or another sub-unit, and then restricts the effective signature of the referenced unit to `signature`.

  - The `(`tag` `identifier` `···`)` references an embedded unit within a signed compound unit. The signature for the `tag` unit must contain a sub-signature that corresponds to the embedded unit, where the sub-signature's name is the initial `identifier`. Additional `identifier`s trace a path into nested sub-signatures to a final embedded unit. The degenerate `(`tag`)` form is equivalent to `tag`.

  - The `((`tag` `identifier` `···`) : `signature`)` form is like the `(`tag` `identifier` `···`)` form except the effective signature of the referenced unit is restricted to `signature`.

The `export` clause determines which variables in the sub-units are re-exported and implicitly determines the export signature of the new compound unit. A signed compound unit can export both individual variables and entire signed units. When an entire signed unit is exported, it becomes an embedded unit of the resulting compound unit.

There are five different forms for specifying exports:

- The `(`var` (unit-path variable))` form exports `variable` from the unit referenced by `unit-path`. The export signature for the signed compound unit includes a `variable` element.

- The `(`var` (unit-path variable) external-variable)` form exports `variable` from the unit referenced by `unit-path`. The export signature for the signed compound unit includes an `external-variable` element.

- The `(`open` unit-path)` form exports variables and embedded units from the referenced unit. The collection of variables that are actually exported depends on the effective signature of the referenced unit:

  - If `unit-path` includes a signature restriction, then only elements from the restricting signature are exported.

  - Otherwise, if the referenced unit is an embedded unit, then only the elements from the associated sub-signature are exported.

  - Otherwise, `unit-path` is just `tag`; in this case, only elements from the signature associated with the `tag` are exported.

  In all cases, the export signature for the signed compound unit includes a copy of each element from the effective signature.

- The `(unit unit-path)` form exports the referenced unit as an embedded unit. The export signature for the signed compound unit includes a sub-signature corresponding to the effective signature from `unit-path`. The name of the sub-signature in the compound unit's export signature depends on `unit-path`:

  - If `unit-path` refers to a tagged import or a sub-unit, then the tag is used for the sub-signature name.

  - Otherwise, the referenced sub-unit was an embedded unit, and the original name for the associated sub-signature is re-used for the export signature's sub-signature.

- The `(unit unit-path identifier)` form exports an embedded unit like `(unit unit-path)` form, but `identifier` is used for the name of the sub-signature in the compound unit's export signature.

The collection of names exported by a compound unit must form a legal signature. This means that all exported names must be distinct.

Run-time checks insure that all `link` clause `expr`s evaluate to a signed unit, and that all linkages match according to the specified signatures:

- If an `expr` evaluates to anything other than a signed unit, the `exn:fail:unit` exception is raised.

- If the export signature for a signed unit does not satisfy the signature specified with its tag, the `exn:fail:unit` exception is raised.

- If the number of units specified in a linkage does not match the number imported by a linking unit, the `exn:fail:unit` exception is raised.

- If the (effective) signature of a provided unit does not match the corresponding import signature, then the `exn:fail:unit` exception is raised.

## 50.8 Invoking Signed Units

Signed units are invoked using the `invoke-unit/sig` form:

      (invoke-unit/sig expr invoke-linkage ···)

    invoke-linkage is one of
      signature
      (identifier : signature)

If the invoked unit requires no imports, the `invoke-unit/sig` form is used in the same way as `invoke-unit`. Otherwise, the `invoke-linkage` signatures must match the import signatures of the signed unit to be invoked. If the signatures match, then variables in the environment of the `invoke-unit/sig` expression are used for immediate linking; the variables used for linking are the ones with names corresponding to the flattened signatures. The signature flattening algorithm is specified in section 50.2; when the `(identifier : signature)` form is used, ``identifier`:` is prefixed onto each variable name in the flattened signature and the prefixed name is used.

`(`define-values/invoke-unit/sig` `signature unit/sig-expr [prefix invoke-linkage `···`]`)` S Y N T A X

This form is the signed-unit version of `define-values/invoke-unit`. The names defined by the expansion of `define-values/invoke-unit/sig` are determined by flattening the `signature` specified before `unit-expr`, then adding the `prefix` (if any). See section 50.2 for more information about signature flattening.

Each `invoke-linkage` is either `signature` or `(identifier : signature)`, as in `invoke-unit/sig`.

`(`namespace-variable-bind/invoke-unit/sig` `signature unit/sig-expr [prefix invoke-linkage `···`]`)` S Y N T A X

This form is the signed-unit version of `namespace-variable-bind/invoke-unit`. See also `define-values/invoke-unit/sig`.

`(`provide-signature-elements` `signature`)` S Y N T A X

Exports from a module every name in the flattened form of `signature`.

## 50.9 Extracting a Primitive Unit from a Signed Unit

The procedure `unit/sig->unit` extracts and returns the primitive unit from a signed unit.

The names exported by the primitive unit correspond to the flattened export signature of the signed unit; see section 50.2 for the flattening algorithm.

The number of import variables for the primitive unit matches the total number of variables in the flattened forms of the signed unit's import signatures. The order of import variables is as follows:

- All of the variables for a single import signature are grouped together, and the relative order of these groups follows the order of the import signatures.

- Within an import signature:

  - variable names are ordered according to ``string<?``;

  - all names from sub-signatures follow the variable names;

  - names from a single sub-signature are grouped together and ordered within the sub-signature group following this algorithm recursively; and

  - the sub-signatures are ordered among themselves using ``string<?`` on the sub-signature names.

## 50.10 Adding a Signature to Primitive Units

The `unit->unit/sig` syntactic form wraps a primitive unit with import and export signatures:

    (unit->unit/sig expr (signature ···) signature)

The last `signature` is used for the export signature and the other `signature`s specify the import signatures. If `expr` does not evaluate to a unit or the unit does not match the signature, no error is reported until the primitive linker discovers the problem.

## 50.11 Expanding Signed Unit Expressions

The `unit/sig`, `compound-unit/sig`, and `invoke-unit/sig` forms expand into expressions using the `unit`, `compound-unit`, and `invoke-unit` forms, respectively.

A signed unit value is represented by a `signed-unit` structure with the following fields:

- `unit` -- the primitive unit implementing the signed unit's content

- `imports` -- the import signatures, represented as a list of pairs, where each pair consists of

  - a tag symbol, used for error reporting; and

  - an ``exploded signature''; an exploded signature is a vector of signature elements, where each element is either

    - a symbol, representing a variable in the signature; or

    - a pair consisting of a symbol and an exploded signature, representing a name sub-signature.

- `exports` -- the export signature, represented as an exploded signature

To perform the signature checking needed by `compound-unit/sig`, MzScheme provides two procedures:

- `(verify-signature-match``` `where exact? dest-context dest-sig src-context src-sig``)` raises an exception unless the exploded signatures `dest-sig` and `src-sig` match. If `exact?` is `#f`, then `src-sig` need only satisfy `dest-sig`, otherwise the signatures must match exactly. The `where` symbol and `dest-context` and `src-context` strings are used for generating an error message string: `where` is used as the name of the signaling procedure and `dest-context` and `src-context` are used as the respective signature names.

  If the match succeeds, void is returned. If the match fails, the `exn:fail:unit` exception is raised for one of the following reasons:

  - The signatures fail to match because `src-sig` is missing an element.

  - The signatures fail to match because `src-sig` contains an extra element.

  - The signatures fail to match because `src-dest` and `src-sig` contain the same element name but for different element types.

- `(verify-linkage-signature-match``` `where tags units export-sigs linking-sigs``)` performs all of the run-time signature checking required by a `compound-unit/sig` or `invoke-unit/sig` expression. The `where` symbol is used for error reporting. The `tags` argument is a list of tag symbols, and the `units` argument is the corresponding list of candidate signed unit values. (The procedure will check whether these values are actually signed unit values.)

  The `export-sigs` list contains one exploded signature for each tag; these correspond to the tag signatures provided in the original `compound-unit/sig` expression. The `linking-sigs` list contains a list of named exploded signatures for each tag (where a ``named signature'' is a pair consisting of a name symbol and an exploded signature); every tag's list corresponds to the signatures that were specified or inferred for the tag's linkage specification in the original `compound-unit/sig` expression. The names on the linking signatures are used for error messages.

  If all linking checks succeed, void is returned. If any check fails, the `exn:fail:unit` exception is raised for one of the following reasons:

  - A value in the `units` list is not a signed unit.

  - The number of import signatures associated with a unit does not agree with the number of linking signatures specified by the corresponding list in `linking-sigs`.

  - A linking signature does not exactly match the signature expected by an importing unit.

`(`signature->symbols` `name`)` S Y N T A X

Expands to the ``exploded'' version (see section 50.11) of the signature bound to `name` (where `name` is an unevaluated identifier).

13 Of course, a signature can be specified as an immediate signature.

14 Element names are compared using the printed form of the name. This is different from any other syntactic form, where variable names are compared as symbols. This distinction is relevant only when source code is generated within Scheme rather than read from a text source.