/
units.scrbl
991 lines (806 loc) · 39.4 KB
/
units.scrbl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
#lang scribble/doc
@(require "mz.rkt" (for-label racket/unit-exptime))
@(define-syntax defkeywords
(syntax-rules (*)
[(_ [* (form ...) as see])
(defform* [form ...]
"Allowed only in a " (racket as) "; see " (racket see) ".")]
[(_ [* (form ...) see-eg])
(defform* [form ...]
"Allowed only in certain forms; see, for example, " (racket see-eg) ".")]
[(_ [form as see])
(defkeywords [* (form) as see])]
[(_ [form see-eg])
(defkeywords [* (form) see-eg])]
[(_ f ...)
(begin (defkeywords f) ...)]))
@title[#:tag "mzlib:unit" #:style 'toc]{Units}
@guideintro["units"]{units}
@deftech{Units} organize a program into separately compilable and
reusable components. The imports and exports of a unit are grouped
into a @deftech{signature}, which can include ``static'' information
(such as macros) in addition to placeholders for run-time values.
Units with suitably matching signatures can be @deftech{linked}
together to form a larger unit, and a unit with no imports can be
@deftech{invoked} to execute its body.
@note-lib[racket/unit #:use-sources (racket/unit)]{ The
@racketmodname[racket/unit] module name can be used as a language name
with @hash-lang[]; see @secref["single-unit"].}
@local-table-of-contents[]
@; ------------------------------------------------------------------------
@section[#:tag "creatingunits"]{Creating Units}
@defform/subs[
#:literals (import export prefix rename only except tag init-depend tag)
(unit
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
init-depends-decl
unit-body-expr-or-defn
...)
([tagged-sig-spec
sig-spec
(tag id sig-spec)]
[sig-spec
sig-id
(prefix id sig-spec)
(rename sig-spec (id id) ...)
(only sig-spec id ...)
(except sig-spec id ...)]
[init-depends-decl
code:blank
(init-depend tagged-sig-id ...)]
[tagged-sig-id
sig-id
(tag id sig-id)])]{
Produces a unit that encapsulates its
@racket[unit-body-expr-or-defn]s. Expressions in the @racket[unit]
body can refer to identifiers bound by the @racket[sig-spec]s of the
@racket[import] clause, and the body must include one definition for
each identifier of a @racket[sig-spec] in the @racket[export] clause.
An identifier that is exported cannot be @racket[set!]ed in either the
defining unit or importing units, although the implicit assignment
to initialize the variable may be visible as a mutation.
Each import or export @racket[sig-spec] ultimately refers to a
@racket[sig-id], which is an identifier that is bound to a signature
by @racket[define-signature]. The @tech{lexical information} of each
identifier imported through a @racket[sig-id] starts with the lexical
information of the @racket[sig-id]; see @racket[define-signature] form
more information.
In a specific import or export position, the set of identifiers bound
or required by a particular @racket[sig-id] can be adjusted in a few
ways:
@itemize[
@item{@racket[(prefix id sig-spec)] as an import binds the same as
@racket[sig-spec], except that each binding is prefixed with @racket[id].
As an export, this form causes definitions using the @racket[id]
prefix to satisfy the exports required by @racket[sig-spec].}
@item{@racket[(rename sig-spec (id id) ...)] as an import binds the
same as @racket[sig-spec], except that the first @racket[id] is used
for the binding instead of the second @racket[id] (where
@racket[sig-spec] by itself must imply a binding that is
@racket[bound-identifier=?] to second @racket[id]). As an export,
this form causes a definition for the first @racket[id] to satisfy
the export named by the second @racket[id] in @racket[sig-spec].}
@item{@racket[(only sig-spec id ...)] as an import binds the same as
@racket[sig-spec], but restricted to just the listed @racket[id]s
(where @racket[sig-spec] by itself must imply a binding that is
@racket[bound-identifier=?] to each @racket[id]). This form is not
allowed for an export.}
@item{@racket[(except sig-spec id ...)] as an import binds the same
as @racket[sig-spec], but excluding all listed @racket[id]s (where
@racket[sig-spec] by itself must imply a binding that is
@racket[bound-identifier=?] to each @racket[id]). This form is not
allowed for an export.}
]
As suggested by the grammar, these adjustments to a signature can be
nested arbitrarily.
A unit's declared imports are matched with actual supplied imports by
signature. That is, the order in which imports are supplied to a unit
when linking is irrelevant; all that matters is the signature
implemented by each supplied import. One actual import must be
provided for each declared import. Similarly, when a unit implements
multiple signatures, the order of the export signatures does not
matter.
To support multiple imports or exports for the same signature, an
import or export can be tagged using the form @racket[(tag
id sig-spec)]. When an import declaration of a unit is
tagged, then one actual import must be given the same tag (with the
same signature) when the unit is linked. Similarly, when an export
declaration is tagged for a unit, then references to that particular
export must explicitly use the tag.
A unit is prohibited syntactically from importing two signatures that
are not distinct, unless they have different tags; two signatures are
@defterm{distinct} only if they share no ancestor through
@racket[extends]. The same syntactic constraint applies to exported
signatures. In addition, a unit is prohibited syntactically from
importing the same identifier twice (after renaming and other
transformations on a @racket[sig-spec]), exporting the same identifier
twice (again, after renaming), or exporting an identifier that is
imported.
When units are linked, the bodies of the linked units are
executed in an order that is specified at the linking site. An
optional @racket[(init-depend tagged-sig-id ...)]
declaration constrains the allowed orders of linking by specifying
that the current unit must be initialized after the unit that supplies
the corresponding import. Each @racket[tagged-sig-id] in an
@racket[init-depend] declaration must have a corresponding import in the
@racket[import] clause.}
@defform/subs[
#:literals (define-syntaxes define-values define-values-for-export
open extends contracted struct)
(define-signature sig-id extension-decl
(sig-elem ...))
([extension-decl
code:blank
(code:line extends sig-id)]
[sig-elem
id
(define-syntaxes (id ...) expr)
(define-values (id ...) expr)
(define-values-for-export (id ...) expr)
(contracted [id contract] ...)
(open sig-spec)
(struct id (field ...) struct-option ...)
(sig-form-id . datum)]
[field id
[id #:mutable]]
[struct-option #:mutable
(code:line #:constructor-name constructor-id)
(code:line #:extra-constructor-name constructor-id)
#:omit-constructor
#:omit-define-syntaxes
#:omit-define-values])]{
Binds an identifier @racket[sig-id] to a signature that specifies a group
of bindings for import or export:
@itemize[
@item{Each @racket[id] in a signature declaration means that a unit
implementing the signature must supply a variable definition for the
@racket[id]. That is, @racket[id] is available for use in units
importing the signature, and @racket[id] must be defined by units
exporting the signature.}
@item{Each @racket[define-syntaxes] form in a signature declaration
introduces a macro that is available for use in any unit that
imports the signature. Free variables in the definition's
@racket[expr] refer to other identifiers in the signature first, or
the context of the @racket[define-signature] form if the signature
does not include the identifier.}
@item{Each @racket[define-values] form in a signature declaration
introduces code that effectively prefixes every unit that imports the
signature. Free variables in the definition's @racket[expr] are
treated the same as for @racket[define-syntaxes].}
@item{Each @racket[define-values-for-export] form in a signature
declaration introduces code that effectively suffixes every unit that
exports the signature. Free variables in the definition's
@racket[expr] are treated the same as for @racket[define-syntaxes].}
@item{Each @racket[contracted] form in a signature declaration means
that a unit exporting the signature must supply a variable definition
for each @racket[id] in that form. If the signature is imported, then
uses of @racket[id] inside the unit are protected by the appropriate
contracts using the unit as the negative blame. If the signature is
exported, then the exported values are protected by the appropriate
contracts which use the unit as the positive blame, but internal uses
of the exported identifiers are not protected. Variables in the
@racket[contract] expressions are treated the same as for
@racket[define-syntaxes].}
@item{Each @racket[(open sig-spec)] adds to the signature everything
specified by @racket[sig-spec].}
@item{Each @racket[(struct id (field ...) struct-option ...)] adds
all of the identifiers that would be bound by the @racket[struct]
form, where the extra option
@racket[#:omit-constructor] omits the constructor identifier.}
@item{Each @racket[(sig-form-id . datum)] extends the signature in a
way that is defined by @racket[sig-form-id], which must be bound by
@racket[define-signature-form]. One such binding is for
@racket[struct/ctc].}
]
When a @racket[define-signature] form includes an @racket[extends]
clause, then the define signature automatically includes everything in
the extended signature. Furthermore, any implementation of the new
signature can be used as an implementation of the extended signature.
The @tech{lexical information} of each @racket[id] within a signature is
compared to the lexical information of @racket[sig-id]. The extra scopes
of @racket[id] relative to @racket[sig-id] are recorded for the
@racket[id]. When the @racket[sig-id] is used as a reference (e.g., in
the @racket[import] clause of @racket[unit]), a variant of @racket[id]
is created for the referencing context by starting with the lexical
information of the referencing @racket[sig-id], and then adding the extra
scopes for @racket[id].}
@defkeywords[[(open sig-spec) _sig-elem define-signature]
[(define-values-for-export (id ...) expr) _sig-elem define-signature]
[(contracted [id contract] ...) _sig-elem define-signature]
[(only sig-spec id ...) _sig-spec unit]
[(except sig-spec id ...) _sig-spec unit]
[(rename sig-spec (id id) ...) _sig-spec unit]
[(prefix id sig-spec) _sig-spec unit]
[(import tagged-sig-spec ...) unit]
[(export tagged-sig-spec ...) unit]
[(link linkage-decl ...) compound-unit]
[* [(tag id sig-spec)
(tag id sig-id)] unit]
[(init-depend tagged-sig-id ...) _init-depend-decl unit]]
@defidform[extends]{
Allowed only within @racket[define-signature].}
@; ------------------------------------------------------------------------
@section[#:tag "invokingunits"]{Invoking Units}
@defform*[#:literals (import)
[(invoke-unit unit-expr)
(invoke-unit unit-expr (import tagged-sig-spec ...))]]{
Invokes the unit produced by @racket[unit-expr]. For each of the
unit's imports, the @racket[invoke-unit] expression must contain a
@racket[tagged-sig-spec] in the @racket[import] clause; see
@racket[unit] for the grammar of @racket[tagged-sig-spec]. If the unit
has no imports, the @racket[import] clause can be omitted.
When no @racket[tagged-sig-spec]s are provided, @racket[unit-expr]
must produce a unit that expects no imports. To invoke the unit, all
bindings are first initialized to the @|undefined-const| value. Next,
the unit's body definitions and expressions are evaluated in order; in
the case of a definition, evaluation sets the value of the
corresponding variable(s). Finally, the result of the last expression
in the unit is the result of the @racket[invoke-unit] expression.
Each supplied @racket[tagged-sig-spec] takes bindings from the
surrounding context and turns them into imports for the invoked unit.
The unit need not declare an import for every provided
@racket[tagged-sig-spec], but one @racket[tagged-sig-spec] must be
provided for each declared import of the unit. For each variable
identifier in each provided @racket[tagged-sig-spec], the value of the
identifier's binding in the surrounding context is used for the
corresponding import in the invoked unit.}
@defform[
#:literals (import export values)
(define-values/invoke-unit unit-expr
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
maybe-results-clause)
#:grammar
([maybe-results-clause (code:line)
(values result-id ...)
(values result-id ... . rest-results-id)])]{
Like @racket[invoke-unit], but the values of the unit's exports are
copied to new bindings.
The unit produced by @racket[unit-expr] is linked and invoked as for
@racket[invoke-unit]. In addition, the @racket[export] clause is
treated as a kind of import into the local definition context. That
is, for every binding that would be available in a unit that used the
@racket[export] clause's @racket[tagged-sig-spec] as an import, a
definition is generated for the context of the
@racket[define-values/invoke-unit] form.
If no @racket[maybe-results-clause] is provided, the unit body may return
any number of values, all of which are ignored. Otherwise, the values
returned from the unit body are bound to the given @racket[result-id]s,
in order. If no @racket[rest-results-id] is provided, the body must return
exactly as many values as there are @racket[result-id]s, but if it is
provided, the body may return arbitrarily many more, and
@racket[rest-results-id] is bound to a list containing the extra results.
@history[#:changed "8.8.0.7" @elem{Added @racket[maybe-results-clause].}]}
@; ------------------------------------------------------------------------
@section[#:tag "compoundunits"]{Linking Units and Creating Compound Units}
@defform/subs[
#:literals (: import export link tag)
(compound-unit
(import link-binding ...)
(export tagged-link-id ...)
(link linkage-decl ...))
([link-binding
(link-id : tagged-sig-id)]
[tagged-link-id
(tag id link-id)
link-id]
[linkage-decl
((link-binding ...) unit-expr tagged-link-id ...)])]{
Links several units into one new compound unit without immediately
invoking any of the linked units. The @racket[unit-expr]s in the
@racket[link] clause determine the units to be linked in creating the
compound unit. The @racket[unit-expr]s are evaluated when the
@racket[compound-unit] form is evaluated.
The @racket[import] clause determines the imports of the compound
unit. Outside the compound unit, these imports behave as for a plain
unit; inside the compound unit, they are propagated to some of the
linked units. The @racket[export] clause determines the exports of the
compound unit. Again, outside the compound unit, these exports are
treated the same as for a plain unit; inside the compound unit, they
are drawn from the exports of the linked units. Finally, the left-hand
and right-hand parts of each declaration in the @racket[link] clause
specify how the compound unit's imports and exports are propagated to
the linked units.
Individual elements of an imported or exported signature are not
available within the compound unit. Instead, imports and exports are
connected at the level of whole signatures. Each specific import or
export (i.e., an instance of some signature, possibly tagged) is given
a @racket[link-id] name. Specifically, a @racket[link-id] is bound by
the @racket[import] clause or the left-hand part of a declaration in
the @racket[link] clause. A bound @racket[link-id] is referenced in
the right-hand part of a declaration in the @racket[link] clause or by
the @racket[export] clause.
The left-hand side of a @racket[link] declaration gives names to each
expected export of the unit produced by the corresponding
@racket[unit-expr]. The actual unit may export additional signatures,
and it may export an extension of a specific signature instead of just
the specified one. If the unit does not export one of the specified
signatures (with the specified tag, if any), the
@exnraise[exn:fail:contract] when the @racket[compound-unit] form is
evaluated.
The right-hand side of a @racket[link] declaration specifies the
imports to be supplied to the unit produced by the corresponding
@racket[unit-expr]. The actual unit may import fewer signatures, and
it may import a signature that is extended by the specified one. If
the unit imports a signature (with a particular tag) that is not
included in the supplied imports, the @exnraise[exn:fail:contract]
when the @racket[compound-unit] form is evaluated. Each
@racket[link-id] supplied as an import must be bound either in the
@racket[import] clause or in some declaration within the @racket[link]
clause.
The order of declarations in the @racket[link] clause determines the
order of invocation of the linked units. When the compound unit is
invoked, the unit produced by the first @racket[unit-expr] is invoked
first, then the second, and so on. If the order specified in the
@racket[link] clause is inconsistent with @racket[init-depend]
declarations of the actual units, then the
@exnraise[exn:fail:contract] when the @racket[compound-unit] form is
evaluated.}
@; ------------------------------------------------------------------------
@section[#:tag "linkinference"]{Inferred Linking}
@defform[
#:literals (import export)
(define-unit unit-id
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
init-depends-decl
unit-body-expr-or-defn
...)
]{
Binds @racket[unit-id] to both a unit and static information about the
unit.
Evaluating a reference to a @racket[unit-id] bound by
@racket[define-unit] produces a unit, just like evaluating an
@racket[_id] bound by @racket[(define _id (unit ...))]. In addition,
however, @racket[unit-id] can be used in @racket[compound-unit/infer].
See @racket[unit] for information on @racket[tagged-sig-spec],
@racket[init-depends-decl], and @racket[unit-body-expr-or-defn].}
@defform/subs[
#:literals (import export link tag :)
(compound-unit/infer
(import tagged-infer-link-import ...)
(export tagged-infer-link-export ...)
(link infer-linkage-decl ...))
([tagged-infer-link-import
tagged-sig-id
(link-id : tagged-sig-id)]
[tagged-infer-link-export
(tag id infer-link-export)
infer-link-export]
[infer-link-export
link-id
sig-id]
[infer-linkage-decl
((link-binding ...) unit-id
tagged-link-id ...)
unit-id])]{
Like @racket[compound-unit]. Syntactically, the difference between
@racket[compound-unit] and @racket[compound-unit/infer] is that the
@racket[_unit-expr] for a linked unit is replaced with a
@racket[unit-id], where a @racket[unit-id] is bound by
@racket[define-unit] (or one of the other unit-binding forms that we
introduce later in this section). Furthermore, an import can name just
a @racket[sig-id] without locally binding a @racket[link-id], and an
export can be based on a @racket[sig-id] instead of a
@racket[link-id], and a declaration in the @racket[link] clause can be
simply a @racket[unit-id] with no specified exports or imports.
The @racket[compound-unit/infer] form expands to
@racket[compound-unit] by adding @racket[sig-id]s as needed to
the @racket[import] clause, by replacing @racket[sig-id]s in the
@racket[export] clause by @racket[link-id]s, and by completing
the declarations of the @racket[link] clause. This completion is based
on static information associated with each
@racket[unit-id]. Links and exports can be inferred when all
signatures exported by the linked units are distinct from each other
and from all imported signatures, and when all imported signatures are
distinct. Two signatures are @defterm{distinct} only if they
share no ancestor through @racket[extends].
The long form of a @racket[link] declaration can be used to resolve
ambiguity by giving names to some of a unit's exports and supplying
specific bindings for some of a unit's imports. The long form need not
name all of a unit's exports or supply all of a unit's imports if the
remaining parts can be inferred.
When a unit declares initialization dependencies,
@racket[compound-unit/infer] checks that the @racket[link] declaration
is consistent with those dependencies, and it reports a syntax error if
not.
Like @racket[compound-unit], the @racket[compound-unit/infer] form
produces a (compound) unit without statically binding information
about the result unit's imports and exports. That is,
@racket[compound-unit/infer] consumes static information, but it does
not generate it. Two additional forms,
@racket[define-compound-unit] and
@racket[define-compound-unit/infer], generate static information
(where the former does not consume static information).
@history[#:changed "6.1.1.8" @elem{Added static checking of the @racket[link]
clause with respect to declared
initialization dependencies.}]}
@defform[
#:literals (import export link)
(define-compound-unit id
(import link-binding ...)
(export tagged-link-id ...)
(link linkage-decl ...))
]{
Like @racket[compound-unit], but binds static information about the
compound unit like @racket[define-unit], including the propagation of
initialization-dependency information (on remaining imports) from the
linked units.}
@defform[
#:literals (import export link)
(define-compound-unit/infer id
(import link-binding ...)
(export tagged-infer-link-export ...)
(link infer-linkage-decl ...))
]{
Like @racket[compound-unit/infer], but binds static information about
the compound unit like @racket[define-compound-unit].}
@defform[
#:literals (import export)
(define-unit-binding unit-id
unit-expr
(import tagged-sig-spec ...+)
(export tagged-sig-spec ...+)
init-depends-decl)
]{
Like @racket[define-unit], but the unit implementation is determined
from an existing unit produced by @racket[unit-expr]. The imports and
exports of the unit produced by @racket[unit-expr] must be consistent
with the declared imports and exports, otherwise the
@exnraise[exn:fail:contract] when the @racket[define-unit-binding]
form is evaluated.}
@defform/subs[
#:literals (link)
(invoke-unit/infer unit-spec)
[(unit-spec unit-id (link link-unit-id ...))]]{
Like @racket[invoke-unit], but uses static information associated with
@racket[unit-id] to infer which imports must be assembled from the
current context. If given a link form containing multiple
@racket[link-unit-id]s, then the units are first linked via
@racket[define-compound-unit/infer].
When assembling imports from the current context, the @tech{lexical
information} of a @racket[unit-id] is used for constructing the lexical
information of the signatures for the unit's imports (i.e., the lexical
information that would normally be derived from the signature reference).
See @racket[define-signature] for more information.}
@defform*[
#:literals (export link values)
[(define-values/invoke-unit/infer
unit-spec
maybe-exports
maybe-results-clause)
(define-values/invoke-unit/infer
(export tagged-sig-spec ...)
unit-spec)]
#:grammar
([unit-spec unit-id (link link-unit-id ...)]
[maybe-exports code:blank (export tagged-sig-spec ...)]
[maybe-results-clause (code:line)
(values result-id ...)
(values result-id ... . rest-results-id)])]{
Like @racket[define-values/invoke-unit], but uses static information
associated with @racket[unit-id] to infer which imports must be
assembled from the current context and, if no @racket[export] clause
is present, which exports should be bound by the definition. If given
a link form containing multiple @racket[link-unit-id]s, then the units
are first linked via @racket[define-compound-unit/infer].
Similar to @racket[invoke-unit/infer], the @tech{lexical information}
of a @racket[unit-id] is used for constructing the lexical information
of the signatures for the unit's inferred imports and inferred exports
(i.e., the lexical information that would normally be derived from a
signature reference). See @racket[define-signature] for more
information.
If @racket[maybe-results-clause] is provided, the values returned by
the unit body are bound in the same way as @racket[define-values/invoke-unit].
For backwards compatibility, an @racket[export] clause is allowed to
appear before @racket[unit-spec] (in which case no @racket[maybe-results-clause]
may be provided). New programs should provide @racket[unit-spec] first
(which is consistent with @racket[define-values/invoke-unit]).
@history[
#:changed "8.8.0.7" @elem{Allowed @racket[unit-spec] to appear before
@racket[maybe-exports] for consistency with @racket[define-values/invoke-unit]
and added @racket[maybe-results-clause].}]}
@; ------------------------------------------------------------------------
@section{Generating A Unit from Context}
@defform[
(unit-from-context tagged-sig-spec)
]{
Creates a unit that implements an interface using bindings in the
enclosing environment. The generated unit is essentially the same as
@racketblock[
(unit
(import)
(export tagged-sig-spec)
(define _id _expr) ...)
]
for each @racket[_id] that must be defined to satisfy the exports, and
each corresponding @racket[_expr] produces the value of @racket[_id] in
the environment of the @racket[unit-from-context] expression. (The unit
cannot be written as above, however, since each @racket[_id] definition
within the unit shadows the binding outside the @racket[unit] form.)
See @racket[unit] for the grammar of @racket[tagged-sig-spec].}
@defform[
(define-unit-from-context id tagged-sig-spec)
]{
Like @racket[unit-from-context], in that a unit is constructed from
the enclosing environment, and like @racket[define-unit], in that
@racket[id] is bound to static information to be used later with inference.}
@; ------------------------------------------------------------------------
@section{Structural Matching}
@defform[
#:literals (import export)
(unit/new-import-export
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
init-depends-decl
((tagged-sig-spec ...) unit-expr tagged-sig-spec))
]{
Similar to @racket[unit], except the body of the unit is determined by
an existing unit produced by @racket[unit-expr]. The result is a unit
whose implementation is @racket[unit-expr], but whose imports,
exports, and initialization dependencies are as in the
@racket[unit/new-import-export] form (instead of as in the unit
produced by @racket[unit-expr]).
The final clause of the @racket[unit/new-import-export] form
determines the connection between the old and new imports and exports.
The connection is similar to the way that @racket[compound-unit]
propagates imports and exports; the difference is that the connection
between @racket[import] and the right-hand side of the link clause is
based on the names of elements in signatures, rather than the names of
the signatures. That is, a @racket[tagged-sig-spec] on the right-hand
side of the link clause need not appear as a @racket[tagged-sig-spec]
in the @racket[import] clause, but each of the bindings implied by the
linking @racket[tagged-sig-spec] must be implied by some
@racket[tagged-sig-spec] in the @racket[import] clause. Similarly,
each of the bindings implied by an @racket[export]
@racket[tagged-sig-spec] must be implied by some left-hand-side
@racket[tagged-sig-spec] in the linking clause.}
@defform[
#:literals (import export)
(define-unit/new-import-export unit-id
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
init-depends-decl
((tagged-sig-spec ...) unit-expr tagged-sig-spec))
]{
Like @racket[unit/new-import-export], but binds static information to
@racket[unit-id] like @racket[define-unit].}
@defform[
#:literals (import export)
(unit/s
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
init-depends-decl
unit-id)]{
Like @racket[unit/new-import-export], but the linking clause is
inferred, so @racket[unit-id] must have the appropriate static
information.}
@defform[
#:literals (import export)
(define-unit/s name-id
(import tagged-sig-spec ...)
(export tagged-sig-spec ...)
init-depends-decl
unit-id)]{
Like @racket[unit/s], but binds static information to @racket[name-id]
like @racket[define-unit].}
@; ------------------------------------------------------------------------
@section[#:tag "define-sig-form"]{Extending the Syntax of Signatures}
@defform*[
[(define-signature-form sig-form-id expr)
(define-signature-form (sig-form-id id) body ...+)
(define-signature-form (sig-form-id id intro-id) body ...+)]
]{
Binds @racket[sig-form-id] for use within a @racket[define-signature]
form.
In the first form, the result of @racket[expr] must be a transformer
procedure that accepts one argument. In the second form, @racket[sig-form-id] is bound to a
transformer procedure whose argument is @racket[id] and whose body is
the @racket[body]s. The third form is like the second one, but
@racket[intro-id] is bound to a procedure that is analogous to @racket[syntax-local-introduce]
for the signature-form expansion.
The result of the transformer procedure must be a list of
syntax objects, which are substituted for a use of
@racket[sig-form-id] in a @racket[define-signature] expansion. (The
result is a list so that the transformer can produce multiple
declarations; @racket[define-signature] has no splicing @racket[begin]
form.)
@history[#:changed "8.1.0.7" @elem{Added support for the form with a transformer
@racket[expr].}]}
@defform/subs[
(struct/ctc id ([field contract-expr] ...) struct-option ...)
([field id
[id #:mutable]]
[struct-option #:mutable
#:omit-constructor
#:omit-define-syntaxes
#:omit-define-values])]{
For use with @racket[define-signature]. The @racket[struct/ctc] form works
similarly to @racket[struct], but the constructor, predicate, field
accessors, and field mutators are contracted appropriately.}
@; ------------------------------------------------------------------------
@section{Unit Utilities}
@defproc[(unit? [v any/c]) boolean?]{
Returns @racket[#t] if @racket[v] is a unit, @racket[#f] otherwise.}
@defform[(provide-signature-elements sig-spec ...)]{
Expands to a @racket[provide] of all identifiers implied by the
@racket[sig-spec]s. See @racket[unit] for the grammar of
@racket[sig-spec].}
@; ------------------------------------------------------------------------
@section[#:tag "unitcontracts"]{Unit Contracts}
@defform/subs[#:literals (import export values init-depend)
(unit/c
(import sig-spec-block ...)
(export sig-spec-block ...)
init-depends-decl
optional-body-ctc)
([sig-spec-block (tagged-sig-spec [id contract] ...)
tagged-sig-spec]
[init-depends-decl
code:blank
(init-depend tagged-sig-id ...)]
[optional-body-ctc
code:blank
contract
(values contract ...)])]{
A @deftech{unit contract} wraps a unit and checks both its imported and
exported identifiers to ensure that they match the appropriate contracts.
This allows the programmer to add contract checks to a single unit value
without adding contracts to the imported and exported signatures.
The unit value must import a subset of the import signatures and export a
superset of the export signatures listed in the unit contract. Additionally,
the unit value must declare initialization dependencies that are a subset of
those specified in the unit contract. Any identifier which is not listed
for a given signature is left alone. Variables used in a given
@racket[contract] expression first refer to other variables in any of the
listed signatures, and then to the context of the @racket[unit/c] expression.
If a body contract is specified then the result of invoking the unit value
is wrapped with the given contract, otherwise the values are returned as-is.
@history[
#:changed "8.8.0.7" @elem{Changed @racket[sig-spec-block] to allow arbitrary
@racket[tagged-sig-spec]s instead of only allowing @racket[tagged-sig-id]s.
Made bindings from @emph{all} signatures visible in the scope of each
@racket[contract] expression instead of only the bindings from the same
signature. Additionally, contracts on signature bindings are enforced
within @racket[contract] expressions.}]}
@defform/subs[#:literals (import export values)
(define-unit/contract unit-id
(import sig-spec-block ...)
(export sig-spec-block ...)
init-depends-decl
optional-body-ctc
unit-body-expr-or-defn
...)
([sig-spec-block (tagged-sig-spec [id contract] ...)
tagged-sig-spec]
[optional-body-ctc
code:blank
(code:line #:invoke/contract contract)
(code:line #:invoke/contract (values contract ...))])]{
The @racket[define-unit/contract] form defines a unit compatible with
link inference whose imports and exports are contracted with a unit
contract. The unit name is used for the positive blame of the contract.
@history[
#:changed "8.8.0.7" @elem{Made bindings from @emph{all} signatures visible in
the scope of each @racket[contract] expression instead of only the bindings
from the same signature. Additionally, contracts on signature bindings are
enforced within @racket[contract] expressions.}]}
@; ------------------------------------------------------------------------
@section[#:tag "single-unit"]{Single-Unit Modules}
When @racketmodname[racket/unit] is used as a language name with
@hash-lang[], the module body is treated as a unit body. The
body must match the following @racket[_module-body] grammar:
@racketgrammar*[
#:literals (import export require begin)
[module-body (code:line
require-decl ...
(import tagged-sig-expr ...)
(export tagged-sig-expr ...)
init-depends-decl
unit-body-expr-or-defn
...)]
[require-decl (require require-spec ...)
(begin require-decl ...)
derived-require-form]]
After any number of @racket[_require-decl]s, the content of the module
is the same as a @racket[unit] body.
The resulting unit is exported as @racket[_base]@racketidfont["@"],
where @racket[_base] is derived from the enclosing module's name
(i.e., its symbolic name, or its path without the directory and file
suffix). If the module name ends in @racketidfont{-unit}, then
@racket[_base] corresponds to the module name before
@racketidfont{-unit}. Otherwise, the module name serves as
@racket[_base].
@; ------------------------------------------------------------------------
@section{Single-Signature Modules}
@defmodulelang[racket/signature]{The @racketmodname[racket/signature]
language treats a module body as a unit signature.}
The body must match the following @racket[_module-body] grammar:
@racketgrammar*[
#:literals (require)
[module-body (code:line (require require-spec ...) ... sig-spec ...)]
]
See @secref["creatingunits"] for the grammar of @racket[_sig-spec].
Unlike the body of a @racketmodname[racket/unit] module, a
@racket[require] in a @racketmodname[racket/signature] module must be
a literal use of @racket[require].
The resulting signature is exported as
@racket[_base]@racketidfont["^"], where @racket[_base] is derived from
the enclosing module's name (i.e., its symbolic name, or its path
without the directory and file suffix). If the module name ends in
@racketidfont{-sig}, then @racket[_base] corresponds to the module
name before @racketidfont{-sig}. Otherwise, the module name serves as
@racket[_base].
A @racket[struct] form as a @racket[_sig-spec] is consistent with the
definitions introduced by @racket[define-struct], as opposed to
definitions introduced by @racket[struct]. (That behavior was originally
a bug, but it is preserved for compatibility.)
@; ----------------------------------------------------------------------
@section{Transformer Helpers}
@defmodule[racket/unit-exptime #:use-sources (racket/unit-exptime)]
The @racketmodname[racket/unit-exptime] library provides procedures
that are intended for use by macro transformers. In particular, the
library is typically imported using @racket[for-syntax] into a module
that defines macro with @racket[define-syntax].
@defproc[(unit-static-signatures [unit-identifier identifier?]
[err-syntax syntax?])
(values (list/c (cons/c (or/c symbol? #f)
identifier?))
(list/c (cons/c (or/c symbol? #f)
identifier?)))]{
If @racket[unit-identifier] is bound to static unit information via
@racket[define-unit] (or other such forms), the result is two
values. The first value is for the unit's imports, and the second is
for the unit's exports. Each result value is a list, where each list
element pairs a symbol or @racket[#f] with an identifier. The symbol
or @racket[#f] indicates the import's or export's tag (where
@racket[#f] indicates no tag), and the identifier indicates the
binding of the corresponding signature.
If @racket[unit-identifier] is not bound to static unit information,
then the @exnraise[exn:fail:syntax]. In that case, the given
@racket[err-syntax] argument is used as the source of the error, where
@racket[unit-identifier] is used as the detail source location.}
@defproc[(signature-members [sig-identifier identifier?]
[err-syntax syntax?])
(values (or/c identifier? #f)
(listof identifier?)
(listof identifier?)
(listof identifier?))]{
If @racket[sig-identifier] is bound to static unit information via
@racket[define-signature] (or other such forms), the result is four
values:
@itemize[
@item{an identifier or @racket[#f] indicating the signature (of any)
that is extended by the @racket[sig-identifier] binding;}
@item{a list of identifiers representing the variables
supplied/required by the signature;}
@item{a list of identifiers for variable definitions in the
signature (i.e., variable bindings that are provided on
import, but not defined by units that implement the
signature); and}
@item{a list of identifiers with syntax definitions in the signature.}
]
Each of the result identifiers is given lexical information that is
based on @racket[sig-identifier], so the names are suitable for
reference or binding in the context of @racket[sig-identifier].
See @racket[define-signature] for more information.
If @racket[sig-identifier] is not bound to a signature, then the
@exnraise[exn:fail:syntax]. In that case, the given
@racket[err-syntax] argument is used as the source of the error, where
@racket[sig-identifier] is used as the detail source location.}
@defproc[(unit-static-init-dependencies [unit-identifier identifier?]
[err-syntax syntax?])
(list/c (cons/c (or/c symbol? #f)
identifier?))]{
If @racket[unit-identifier] is bound to static unit information via
@racket[define-unit] (or other such forms), the result is a list of
pairs. Each pair combines a tag (or @racket[#f] for no tag) and a
signature name, indicating an initialization dependency of the unit on
the specified import (i.e., the same tag and signature are included in
the first result from @racket[unit-static-signatures]).
If @racket[unit-identifier] is not bound to static unit information,
then the @exnraise[exn:fail:syntax]. In that case, the given
@racket[err-syntax] argument is used as the source of the error, where
@racket[unit-identifier] is used as the detail source location.
@history[#:added "6.1.1.8"]}