14.4 First-Class Units
The define-unit form combines define with a
unit form, similar to the way that (define (f x) ....) combines define followed by an identifier with an
implicit lambda.
Expanding the shorthand, the definition of toy-store@ could
almost be written as
A difference between this expansion and define-unit is that
the imports and exports of toy-store@ cannot be
inferred. That is, besides combining define and
unit, define-unit attaches static information to the
defined identifier so that its signature information is available
statically to define-values/invoke-unit/infer and other
forms.
Despite the drawback of losing static signature information,
unit can be useful in combination with other forms that work
with first-class values. For example, we could wrap a unit
that creates a toy store in a lambda to supply the store’s
color:
"toy-store-maker.rkt"
To invoke a unit created by toy-store@-maker, we must use
define-values/invoke-unit, instead of the
/infer variant:
In the define-values/invoke-unit form, the (import toy-factory^) line takes bindings from the current context that match
the names in toy-factory^ (the ones that we created by
invoking simple-factory@), and it supplies them as imports to
toy-store@. The (export toy-store^) clause indicates
that the unit produced by toy-store@-maker will export
toy-store^, and the names from that signature are defined
after invoking the unit.
To link a unit from toy-store@-maker, we can use the
compound-unit form:
> (require "store-specific-factory-unit.rkt") |
|
|
|
This compound-unit form packs a lot of information into one
place. The left-hand-side TF and TS in the
link clause are binding identifiers. The identifier
TF is essentially bound to the elements of
toy-factory^ as implemented by
store-specific-factory@. The identifier TS is
similarly bound to the elements of toy-store^ as implemented
by toy-store@. Meanwhile, the elements bound to TS
are supplied as imports for store-specific-factory@, since
TS follows store-specific-factory@. The elements
bound to TF are similarly supplied to
toy-store@. Finally, (export TF TS) indicates that
the elements bound to TF and TS are exported from
the compound unit.
The above compound-unit form uses
store-specific-factory@ as a first-class unit, even though
its information could be inferred. Every unit can be used as a
first-class unit, in addition to its use in inference contexts. Also,
various forms let a programmer bridge the gap between inferred and
first-class worlds. For example, define-unit-binding binds a
new identifier to the unit produced by an arbitrary expression; it
statically associates signature information to the identifier, and it
dynamically checks the signatures against the first-class unit
produced by the expression.