forked from cisco/ChezScheme
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsyntax.stex
1983 lines (1696 loc) · 73.3 KB
/
syntax.stex
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
992
993
994
995
996
997
998
999
1000
% Copyright 2005-2017 Cisco Systems, Inc.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
\chapter{Syntactic Extension and Modules\label{CHPTSYNTAX}}
This chapter describes the {\ChezScheme} extensions to the
syntax-case syntactic abstraction mechanism now standardized in
the Revised$^6$ Report.
These extensions include
the module system (Section~\ref{SECTSYNTAXMODULES}),
meta definitions (Section~\ref{SECTSYNTAXMETA}),
conditional expansion (Section~\ref{SECTSYNTAXMETACOND})
\scheme{syntax-rules} fenders,
\scheme{fluid-let-syntax},
and \scheme{include}.
\section{Fluid Keyword Bindings\label{SECTSYNTAXDEFINITIONS}}
Keyword bindings established via the Revised$^6$ Report
\scheme{define-syntax}, \scheme{let-syntax}, or \scheme{letrec-syntax}
forms may be rebound temporarily with \scheme{fluid-let-syntax}.
%----------------------------------------------------------------------------
\entryheader
\formdef{fluid-let-syntax}{\categorysyntax}{(fluid-let-syntax ((\var{keyword} \var{expr}) \dots) \var{form_1} \var{form_2} \dots)}
\returns see explanation
\listlibraries
\endentryheader
\noindent
Each \var{expr} must evaluate to a transformer.
\scheme{fluid-let-syntax} is similar to the standard \scheme{let-syntax}, except
that instead of introducing new bindings for the keywords
\scheme{\var{keyword} \dots},
\scheme{fluid-let-syntax} temporarily alters the existing bindings
for the keywords during the expansion of its body.
That is, during the expansion of \scheme{\var{form_1} \var{form_2} \dots},
the visible lexical (or top-level) binding
for each \scheme{keyword} is temporarily replaced by a new association
between the keyword and the corresponding transformer.
This affects any references to the keyword that resolve
to the same lexical (or top-level) binding whether the references occur
in the text of the body or are introduced during its expansion.
In contrast, \scheme{let-syntax} captures only those references that
occur within the text of its body.
The following example shows how \scheme{fluid-let-syntax}
differs from \scheme{let-syntax}.
\schemedisplay
(let ([f (lambda (x) (+ x 1))])
(let-syntax ([g (syntax-rules ()
[(_ x) (f x)])])
(let-syntax ([f (syntax-rules ()
[(_ x) x])])
(g 1)))) ;=> 2
(let ([f (lambda (x) (+ x 1))])
(let-syntax ([g (syntax-rules ()
[(_ x) (f x)])])
(fluid-let-syntax ([f (syntax-rules ()
[(_ x) x])])
(g 1)))) ;=> 1
\endschemedisplay
\noindent
The two expressions are identical except that the inner
\scheme{let-syntax} form
in the first expression is a \scheme{fluid-let-syntax} form in the second.
In the first expression, the \scheme{f} occurring in the expansion of
\scheme{(g 1)} refers to
the \scheme{let}-bound variable \scheme{f}, whereas in the second it refers
to the keyword \scheme{f} by virtue of the fluid syntax binding for
\scheme{f}.
\index{integrable procedures}\index{\scheme{define-integrable}}%
The following code employs \scheme{fluid-let-syntax} in the definition
of a \scheme{define-integrable} form that is similar
to \scheme{define} for procedure definitions except that it causes the
code for the procedure to be \emph{integrated}, or inserted, wherever
a direct call to the procedure is found.
No semantic difference is visible between procedures defined with
\scheme{define-integrable} and those defined with \scheme{define}, except that
a top-level \scheme{define-integrable} form must appear before the first
reference to the defined identifier.
Lexical scoping is preserved, the actual parameters
in an integrated call are evaluated once and at the proper time,
integrable procedures may be used as first-class values, and
recursive procedures do not cause indefinite recursive expansion.
\schemedisplay
(define-syntax define-integrable
(syntax-rules (lambda)
[(_ name (lambda formals form1 form2 ...))
(begin
(define xname
(fluid-let-syntax ([name (identifier-syntax xname)])
(lambda formals form1 form2 ...)))
(define-syntax name
(lambda (x)
(syntax-case x ()
[_ (identifier? x) #'xname]
[(_ arg (... ...))
#'((fluid-let-syntax ([name (identifier-syntax xname)])
(lambda formals form1 form2 ...))
arg
(... ...))]))))]))
\endschemedisplay
\noindent
A \scheme{define-integrable} has the following form.
\schemedisplay
(define-integrable \var{name} \var{lambda-expression})
\endschemedisplay
\noindent
A \scheme{define-integrable} form expands into a pair of definitions: a syntax
definition of \var{name} and a variable definition of \scheme{xname}.
The transformer for \var{name} converts apparent calls to
\var{name} into direct calls to \var{lambda-expression}.
Since the resulting forms are merely direct \scheme{lambda} applications
(the equivalent of \scheme{let} expressions),
the actual parameters are evaluated exactly once and before evaluation
of the procedure's body, as required.
All other references to \var{name} are replaced with references to
\scheme{xname}.
The definition of \scheme{xname} binds it to the value of
\var{lambda-expression}.
This allows the procedure to be used as a first-class value.
Because \scheme{xname} is introduced by the transformer, the binding for
\scheme{xname} is not visible anywhere except where references to it
are introduced by the transformer for \var{name}.
Within \var{lambda-expression}, wherever it appears, \var{name}
is rebound to a transformer that expands all references into references
to \scheme{xname}.
The use of \index{\scheme{fluid-let-syntax}}\scheme{fluid-let-syntax}
for this purpose prevents indefinite
expansion from indirect recursion among integrable procedures.
This allows the procedure to be recursive without causing indefinite
expansion.
Nothing special is done by \scheme{define-integrable} to maintain lexical
scoping, since lexical scoping is maintained automatically by the
expander.
{\ChezScheme} integrate locally defined procedures automatically when it is
appropriate to do so.
It cannot integrate procedures defined at top-level,
however, since code that assigns top-level variables can be introduced
into the system (via \scheme{eval} or \scheme{load}) at any time.
\scheme{define-integrable} can be used to force the integration of
procedures bound at top-level, even if the integration of locally bound
procedures is left to the compiler.
It can also be used to force the integration of large procedures that
the compiler would not normally integrate.
(The \scheme{expand/optimize} procedure is useful for determining when
integration does or does not take place.)
\section{Syntax-Rules Transformers\label{SECTSYNTAXRULES}}
{\ChezScheme} extends \scheme{syntax-rules} to permit clause to include
fenders just like those allowed within \scheme{syntax-case} clauses.
%----------------------------------------------------------------------------
\entryheader
\formdef{syntax-rules}{\categorysyntax}{(syntax-rules (\var{literal} \dots) \var{clause} \dots)}
\returns a transformer
\listlibraries
\endentryheader
\noindent
Each \index{literals}\var{literal} must be an identifier other than
an underscore (~\scheme{_}~) or ellipsis (~\scheme{...}~).
Each clause must take the form below.
\schemedisplay
(\var{pattern} \var{template})
(\var{pattern} \var{fender} \var{template})
\endschemedisplay
\noindent
The first form is the only form supported by the Revised$^6$ Report.
\section{Syntax-Case Transformers\label{SECTSYNTAXCASE}}
{\ChezScheme} provides several procedures and syntactic forms that may
be used to simplify the coding of certain syntactic abstractions.
%----------------------------------------------------------------------------
\entryheader
\formdef{syntax->list}{\categoryprocedure}{(syntax->list \var{syntax-object})}
\returns a list of syntax objects
\listlibraries
\endentryheader
\noindent
This procedure takes a syntax object representing
a list-structured form and returns a list of syntax objects, each representing
the corresponding subform of the input form.
%Programmers are encouraged to use this procedure even when the current
%{\ChezScheme} implementation of \scheme{syntax-case} guarantees that
%the output of a \scheme{syntax} form is a list, since future versions of
%{\ChezScheme} may remove these guarantees in the interest of maintaining
%better source information.
\scheme{syntax->list} may be defined as follows.
\schemedisplay
(define syntax->list
(lambda (ls)
(syntax-case ls ()
[() '()]
[(x . r) (cons #'x (syntax->list #'r))])))
#'(a b c) ;=> #<syntax (a b c)>
(syntax->list #'(a b c)) ;=> (#<syntax a> #<syntax b> #<syntax c>)
\endschemedisplay
\scheme{syntax->list} is not required for list structures constructed
from individual pattern variable values or sequences of pattern-variable
values, since such structures are already lists.
For example:
\schemedisplay
(list? (with-syntax ([x #'a] [y #'b] [z #'c]) #'(x y z)))) ;=> #t
(list? (with-syntax ([(x ...) #'(a b c)]) #'(x ...))) ;=> #t
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{syntax->vector}{\categoryprocedure}{(syntax->vector \var{syntax-object})}
\returns a list of syntax objects
\listlibraries
\endentryheader
\noindent
This procedure takes a syntax object representing
a vector-structured form and returns a list of syntax objects, each representing
the corresponding subform of the input form.
%Programmers are encouraged to use this procedure even when the current
%{\ChezScheme} implementation of \scheme{syntax-case} guarantees that
%the output of a \scheme{syntax} form is a vector, since future versions of
%{\ChezScheme} may remove these guarantees in the interest of maintaining
%better source information.
\scheme{syntax->vector} may be defined as follows.
\schemedisplay
(define syntax->vector
(lambda (v)
(syntax-case v ()
[#(x ...) (apply vector (syntax->list #'(x ...)))])))
#'#(a b c) ;=> #<syntax #(a b c)>
(syntax->vector #'#(a b c)) ;=> #(#<syntax a> #<syntax b> #<syntax c>)
\endschemedisplay
\scheme{syntax->vector} is not required for vector structures constructed
from individual pattern variable values or sequences of pattern-variable
values, since such structures are already vectors.
For example:
\schemedisplay
(vector? (with-syntax ([x #'a] [y #'b] [z #'c]) #'#(x y z)))) ;=> #t
(vector? (with-syntax ([(x ...) #'(a b c)]) #'#(x ...))) ;=> #t
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{syntax-object->datum}{\categoryprocedure}{(syntax-object->datum \var{obj})}
\returns \var{obj} stripped of syntactic information
\listlibraries
\endentryheader
\noindent
\scheme{syntax-object->datum} is identical to the Revised$^6$ Report
\scheme{syntax->datum}.
%----------------------------------------------------------------------------
\entryheader
\formdef{datum}{\categorysyntax}{(datum \var{template})}
\returns see below
\listlibraries
\endentryheader
\scheme{(datum \var{template})} is a convenient shorthand syntax for
\schemedisplay
(syntax->datum (syntax \var{template}))
\endschemedisplay
\var{datum} may be defined simply as follows.
\schemedisplay
(define-syntax datum
(syntax-rules ()
[(_ t) (syntax->datum #'t)]))
(with-syntax ((a #'(a b c))) (datum a)) ;=> (a b c)
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{datum->syntax-object}{\categoryprocedure}{(datum->syntax-object \var{template-identifier} \var{obj})}
\returns a syntax object
\listlibraries
\endentryheader
\scheme{datum->syntax-object} is identical to the Revised$^6$ Report
\scheme{datum->syntax}.
%----------------------------------------------------------------------------
\entryheader
\formdef{with-implicit}{\categorysyntax}{(with-implicit (\var{id_0} \var{id_1} \dots) \var{body_1} \var{body_2} \dots)}
\returns see below
\listlibraries
\endentryheader
This form abstracts over the common usage of \scheme{datum->syntax}
for creating implicit identifiers (see above).
The form
\schemedisplay
(with-implicit (\var{id_0} \var{id_1} \dots)
\var{body_1} \var{body_2} \dots)
\endschemedisplay
is equivalent to
\schemedisplay
(with-syntax ([\var{id_1} (datum->syntax #'\var{id_0} '\var{id_1})] \dots)
\var{body_1} \var{body_2} \dots)
\endschemedisplay
\scheme{with-implicit} can be defined simply as follows.
\schemedisplay
(define-syntax with-implicit
(syntax-rules ()
[(_ (tid id ...) b1 b2 ...)
(with-syntax ([id (datum->syntax #'tid 'id)] ...)
b1 b2 ...)]))
\endschemedisplay
We can use \scheme{with-implicit} to simplify the (correct version of)
\scheme{loop} above.
\schemedisplay
(define-syntax loop
(lambda (x)
(syntax-case x ()
[(k e ...)
(with-implicit (k break)
#'(call-with-current-continuation
(lambda (break)
(let f () e ... (f)))))])))
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{include}{\categorysyntax}{(include \var{path})}
\returns unspecified
\listlibraries
\endentryheader
\noindent
\var{path} must be a string.
\scheme{include} expands into a \scheme{begin} expression containing
the forms found in the file named by \var{path}.
For example, if the file \scheme{f-def.ss} contains
% the expression
\scheme{(define f (lambda () x))}, the expression
\schemedisplay
(let ([x "okay"])
(include "f-def.ss")
(f))
\endschemedisplay
\noindent
evaluates to \scheme{"okay"}.
An include form is treated as a definition if it appears within a
sequence of definitions and the forms on the file named by
\var{path} are all definitions, as in the above example.
If the file contains expressions instead, the \scheme{include} form is
treated as an expression.
\scheme{include} may be defined portably as follows, although
{\ChezScheme} uses an implementation-dependent definition that allows
it to capture and maintain source information for included code.
\schemedisplay
(define-syntax include
(lambda (x)
(define read-file
(lambda (fn k)
(let ([p (open-input-file fn)])
(let f ([x (read p)])
(if (eof-object? x)
(begin (close-input-port p) '())
(cons (datum->syntax k x)
(f (read p))))))))
(syntax-case x ()
[(k filename)
(let ([fn (datum filename)])
(with-syntax ([(exp ...) (read-file fn #'k)])
#'(begin exp ...)))])))
\endschemedisplay
\noindent
The definition of \scheme{include} uses \scheme{datum->syntax} to convert
the objects read from the file into syntax objects in the proper
lexical context, so that identifier references and definitions within
those expressions are scoped where the \scheme{include} form appears.
In {\ChezScheme}'s implementation of \scheme{include},
the parameter \scheme{source-directories} (Section~\ref{SECTSYSTEMSOURCE})
determines the set of directories searched for source files not identified
by absolute path names.
%----------------------------------------------------------------------------
\entryheader\label{desc:syntax-error}
\formdef{syntax-error}{\categoryprocedure}{(syntax-error \var{obj} \var{string} \dots)}
\returns does not return
\listlibraries
\endentryheader
Syntax errors may be reported with \scheme{syntax-error}, which produces
a message by concatenating \scheme{\var{string} \dots} and a printed
representation of \var{obj}.
If no string arguments are provided, the string \scheme{"invalid syntax"}
is used instead.
When \var{obj} is a syntax object, the syntax-object wrapper is
stripped (as with \scheme{syntax->datum}) before the printed representation
is created.
If source file information is present in the syntax-object wrapper,
\scheme{syntax-error} incorporates this information into the error
message.
\scheme{syntax-case} and \scheme{syntax-rules} call \scheme{syntax-error}
automatically if the input fails to match one of the clauses.
We can use \scheme{syntax-error} to precisely report the cause
of the errors detected in the following definition of
(unnamed) \scheme{let}.
\schemedisplay
(define-syntax let
(lambda (x)
(define check-ids!
(lambda (ls)
(unless (null? ls)
(unless (identifier? (car ls))
(syntax-error (car ls) "let cannot bind non-identifier"))
(check-ids! (cdr ls)))))
(define check-unique!
(lambda (ls)
(unless (null? ls)
(let ([x (car ls)])
(when (let mem? ([ls (cdr ls)])
(and (not (null? ls))
(or (bound-identifier=? x (car ls))
(mem? (cdr ls)))))
(syntax-error x "let cannot bind two occurrences of")))
(check-unique! (cdr ls)))))
(syntax-case x ()
[(_ ((i e) ...) b1 b2 ...)
(begin
(check-ids! #'(i ...))
(check-unique! #'(i ...))
#'((lambda (i ...) b1 b2 ...) e ...))])))
\endschemedisplay
With this change, the expression
\schemedisplay
(let ([a 3] [a 4]) (+ a a))
\endschemedisplay
produces the error message ``let cannot bind two occurrences of \scheme{a}.''
%----------------------------------------------------------------------------
\entryheader
\formdef{literal-identifier=?}{\categoryprocedure}{(literal-identifier=? \var{identifier_1} \var{identifier_2})}
\returns see below
\listlibraries
\endentryheader
This procedure is identical to the Revised$^6$ Report
\scheme{free-identifier=?}, and is provided for backward
compatibility only.
\section{Compile-time Values and Properties\label{SECTSYNTAXCTVS}}
When defining sets of dependent macros, it is often convenient to attach
information to identifiers in the same \emph{compile time environment}
that the expander uses to record information about variables, keywords,
module names, etc.
For example, a record-type definition macro, like
\scheme{define-record-type}, might need to attach information to the
record-type name in the compile-time environment for use in handling child
record-type definitions.
{\ChezScheme} provides two mechanisms for attaching information to
identifiers in the compile-time environment: compile-time values and
compile-time properties.
A compile-time value is a kind of transformer that can be
associated with an identifier via \scheme{define-syntax},
\scheme{let-syntax}, \scheme{letrec-syntax}, and \scheme{fluid-let-syntax}.
When an identifier is associated with a compile-time value, it cannot
also have any other meaning, and an attempt to reference it as an
ordinary identifier results in a syntax error.
A compile-time property, on the other hand, is maintained alongside
an existing binding, providing additional information about the
binding.
Properties are ignored when ordinary references to an identifier
occur.
The mechanisms used by a macro to obtain compile-time values and
properties are similar.
In both cases, the macro's transformer returns a procedure \var{p}
rather than a syntax object.
The expander invokes \var{p} with one argument, an environment-lookup
procedure \var{lookup}, which \var{p} can then use to obtain compile-time
values and properties for one or more identifiers before it constructs the
macro's final output.
\var{lookup} accepts one or two identifier arguments.
With one argument, \var{id}, \var{lookup} returns the compile-time
value of \var{id}, or \scheme{#f} if \var{id} has no compile-time value.
With two arguments, \var{id} and \var{key}, \var{lookup} returns the
value of \var{id}'s \var{key} property, or \scheme{#f} if \var{id}
has no \var{key} property.
%----------------------------------------------------------------------------
\entryheader
\formdef{make-compile-time-value}{\categoryprocedure}{(make-compile-time-value \var{obj})}
\returns a compile-time value
\listlibraries
\endentryheader
A compile time value is a kind of transformer with which a keyword may
be associated by any of the keyword binding constructs, e.g., \scheme{define-syntax}
or \scheme{let-syntax}.
The transformer encapsulates the supplied \var{obj}.
The encapsulated object may be retrieved as described above.
The following example illustrates how this feature might be used to define
a simple syntactic record-definition mechanism where the record type descriptor
is generated at expansion time.
\schemedisplay
(define-syntax drt
(lambda (x)
(define construct-name
(lambda (template-identifier . args)
(datum->syntax template-identifier
(string->symbol
(apply string-append
(map (lambda (x)
(if (string? x)
x
(symbol->string (syntax->datum x))))
args))))))
(define do-drt
(lambda (rname fname* prtd)
(with-syntax ([rname rname]
[rtd (make-record-type-descriptor
(syntax->datum rname) prtd #f #f #f
(list->vector
(map (lambda (fname)
`(immutable ,(syntax->datum fname)))
fname*)))]
[make-rname (construct-name rname "make-" rname)]
[rname? (construct-name rname rname "?")]
[(rname-fname ...)
(map (lambda (fname)
(construct-name fname rname "-" fname))
fname*)]
[(i ...) (enumerate fname*)])
#'(begin
(define-syntax rname (make-compile-time-value 'rtd))
(define rcd (make-record-constructor-descriptor 'rtd #f #f))
(define make-rname (record-constructor rcd))
(define rname? (record-predicate 'rtd))
(define rname-fname (record-accessor 'rtd i))
...))))
(syntax-case x (parent)
[(_ rname (fname ...))
(for-all identifier? #'(rname fname ...))
(do-drt #'rname #'(fname ...) #f)]
[(_ rname pname (fname ...))
(for-all identifier? #'(rname pname fname ...))
(lambda (lookup)
(let ([prtd (lookup #'pname)])
(unless (record-type-descriptor? prtd)
(syntax-error #'pname "unrecognized parent record type"))
(do-drt #'rname #'(fname ...) prtd)))])))
\endschemedisplay
\schemedisplay
(drt prec (x y))
(drt crec prec (z))
(define r (make-crec 1 2 3))
(prec? r) ;=> #t
(prec-x r) ;=> 1
(crec-z r) ;=> 3
prec ;=> \var{exception: invalid syntax prec}
\endschemedisplay
%----------------------------------------------------------------------------
\entryheader
\formdef{define-property}{\categorysyntax}{(define-property \var{id} \var{key} \var{expr})}
\returns unspecified
\listlibraries
\endentryheader
A \scheme{define-property} form attaches a property to an
existing identifier binding without disturbing the existing meaning
of the identifier in the scope of that binding.
It is typically used by one macro to record information about a binding
for use by another macro.
Both \var{id} and \var{key} must be identifiers.
The expression \var{expr} is evaluated when the \scheme{define-property}
form is expanded, and a new property associating \var{key} with the
value of \var{expr} is attached to the existing binding of
\var{id}, which must have a visible local or top-level binding.
\scheme{define-property} is a definition and can appear anywhere
other definitions can appear.
The scope of a property introduced by \scheme{define-property} is the
entire body in which the \scheme{define-property} form appears or global
if it appears at top level, except
where it is replaced by a property for the same \var{id} and
\var{key} or where the binding to which it is attached is shadowed.
Any number of properties can be attached to the same binding with
different keys.
Attaching a new property with the same name as an property already
attached to a binding shadows the existing property with the new
property.
The following example defines a macro, \scheme{get-info}, that retrieves
the \scheme{info} property of a binding, defines the variable \scheme{x},
attaches an \scheme{info} property to the binding of \scheme{x}, retrieves
the property via \scheme{get-info}, references \scheme{x} to show that
its normal binding is still intact, and uses \scheme{get-info} again
within the scope of a different binding of \scheme{x} to show that the
properties are shadowed as well as the outer binding of \scheme{x}.
\schemedisplay
(define info)
(define-syntax get-info
(lambda (x)
(lambda (lookup)
(syntax-case x ()
[(_ q)
(let ([info-value (lookup #'q #'info)])
#`'#,(datum->syntax #'* info-value))]))))
(define x "x-value")
(define-property x info "x-info")
(get-info x) ;=> "x-info"
x ;=> "x-value"
(let ([x "inner-x-value"]) (get-info x)) ;=> #f
\endschemedisplay
For debugging, it is often useful to have a form that retrieves
an arbitrary property, given an identifier and a key.
The \index{\scheme{get-property}}\scheme{get-property} macro below does
just that.
\schemedisplay
(define-syntax get-property
(lambda (x)
(lambda (r)
(syntax-case x ()
[(_ id key)
#`'#,(datum->syntax #'* (r #'id #'key))]))))
(get-property x info) ;=> "x-info"
\endschemedisplay
The bindings for both identifiers must be visible where
\scheme{get-property} is used.
The version of \scheme{drt} defined below is like the one defined using
\scheme{make-compile-time-value} above, except that it defines the
record name as a macro that raises an exception with a more descriptive
message, while attaching the record type descriptor to the binding as a
separate property.
The variable \scheme{drt-key} defined along with \scheme{drt} is used
only as the key for the property that \scheme{drt} attaches to a record
name.
Both \scheme{drt-key} and \scheme{drt} are defined within a module that
exports only the latter, ensuring that the properties used by \scheme{drt}
cannot be accessed or forged.
\schemedisplay
(library (drt) (export drt) (import (chezscheme))
(define drt-key)
(define-syntax drt
(lambda (x)
(define construct-name
(lambda (template-identifier . args)
(datum->syntax template-identifier
(string->symbol
(apply string-append
(map (lambda (x)
(if (string? x)
x
(symbol->string (syntax->datum x))))
args))))))
(define do-drt
(lambda (rname fname* prtd)
(with-syntax ([rname rname]
[rtd (make-record-type-descriptor
(syntax->datum rname) prtd #f #f #f
(list->vector
(map (lambda (fname)
`(immutable ,(syntax->datum fname)))
fname*)))]
[make-rname (construct-name rname "make-" rname)]
[rname? (construct-name rname rname "?")]
[(rname-fname ...)
(map (lambda (fname)
(construct-name fname rname "-" fname))
fname*)]
[(i ...) (enumerate fname*)])
#'(begin
(define-syntax rname
(lambda (x)
(syntax-error x "invalid use of record name")))
(define rcd (make-record-constructor-descriptor 'rtd #f #f))
(define-property rname drt-key 'rtd)
(define make-rname (record-constructor rcd))
(define rname? (record-predicate 'rtd))
(define rname-fname (record-accessor 'rtd i))
...))))
(syntax-case x (parent)
[(_ rname (fname ...))
(for-all identifier? #'(rname fname ...))
(do-drt #'rname #'(fname ...) #f)]
[(_ rname pname (fname ...))
(for-all identifier? #'(rname pname fname ...))
(lambda (lookup)
(let ([prtd (lookup #'pname #'drt-key)])
(unless prtd
(syntax-error #'pname "unrecognized parent record type"))
(do-drt #'rname #'(fname ...) prtd)))]))))
\endschemedisplay
\schemedisplay
(import (drt))
(drt prec (x y))
(drt crec prec (z))
(define r (make-crec 1 2 3))
(prec? r) ;=> #t
(prec-x r) ;=> 1
(crec-z r) ;=> 3
prec ;=> \var{exception: invalid use of record name prec}
\endschemedisplay
\section{Modules\label{SECTSYNTAXMODULES}}
\index{modules}Modules are used to help organize programs into separate
parts that interact cleanly via declared interfaces.
Although modular programming is typically used to facilitate the development
of large programs possibly written by many individuals, it may also be
used in {\ChezScheme} at a ``micro-modular'' level, since {\ChezScheme}
module and import forms are definitions and may appear anywhere any other
kind of definition may appear, including within a \scheme{lambda} body
or other local scope.
Modules control visibility of bindings and can be viewed as extending
lexical scoping to allow more precise control over where bindings are
or are not visible.
Modules export identifier bindings, i.e., variable bindings, keyword
bindings, or module name bindings.
Modules may be \emph{named} or \emph{anonymous}.
Bindings exported from a named module may be made visible via an import
form wherever the module's name is visible.
Bindings exported from an anonymous module are implicitly imported where
the module form appears.
Anonymous modules are useful for hiding some of a set of bindings while
allowing the remaining bindings in the set to be visible.
Some of the text and examples given in this section are
adapted from the paper
``Extending the scope of syntactic
abstraction''~\cite{waddell:modules}, which describes modules and their
implementation in more detail.
%----------------------------------------------------------------------------
\entryheader
\formdef{module}{\categorysyntax}{(module \var{name} \var{interface} \var{defn} \dots \var{init} \dots)}
\formdef{module}{\categorysyntax}{(module \var{interface} \var{defn} \dots \var{init} \dots)}
\returns unspecified
\listlibraries
\endentryheader
\noindent
\var{name} is an identifier, \scheme{\var{defn} \dots}
are definitions, and \scheme{\var{init} \dots} are expressions.
\var{interface} is a list of exports \scheme{(\var{export} \dots)},
where each \var{export} is either an identifier \var{identifier}
or of the form \scheme{(\var{identifier} \var{export} \dots)}.
The first syntax for \scheme{module} establishes a named scope that
encapsulates a set of identifier bindings.
The exported bindings may be made visible via \scheme{import} or
\scheme{import-only} (Section~\ref{SECTLIBRARYIMPORTEXPORTFORMS})
anywhere the module name is visible.
The second syntax for \scheme{module} introduces an anonymous module
whose bindings are implicitly imported (as if by \scheme{import} of a
hidden module name) where the module form appears.
A module consists of a (possibly empty) set of
definitions and a (possibly empty) sequence of initialization expressions.
The identifiers defined within a module are visible within the body
of the module and, if exported, within the scope of an import for the
module.
Each identifier listed in a module's interface must be defined within
or imported into that module.
A \scheme{module} form is a definition and can appear anywhere other
definitions can appear, including
at the top level of a program, nested within the bodies of
\scheme{lambda} expressions, nested within \scheme{library} and
top-level program forms, and nested within other modules.
Also, because module names are scoped like other identifiers,
modules and libraries may export module names as well as variables and keywords.
When an interface contains an export of the form
\scheme{(\var{identifier} \var{export} \dots)}, only \var{identifier} is
visible in the importing context.
The identifiers within \scheme{\var{export} \dots} are
\emph{indirect imports}, as if declared via an
\scheme{indirect-export} form (Section~\ref{SECTLIBRARYIMPORTEXPORTFORMS}).
Module names occupy the same namespace as other identifiers and follow
the same scoping rules.
Unless exported, identifiers defined within a module are visible only
within that module.
Expressions within a module can reference identifiers bound outside of
the module.
\schemedisplay
(let ([x 3])
(module m (plusx)
(define plusx (lambda (y) (+ x y))))
(import m)
(let ([x 4])
(plusx 5))) ;=> 8
\endschemedisplay
\noindent
Similarly, \scheme{import} does not prevent access to identifiers that
are visible where the import form appears, except for those variables
shadowed by the imported identifiers.
\schemedisplay
(module m (y) (define y 'm-y))
(let ([x 'local-x] [y 'local-y])
(import m)
(list x y)) ;=> (local-x m-y)
\endschemedisplay
On the other hand, use of \scheme{import-only} within a module
establishes an isolated scope in
which the only visible identifiers are those exported by the
imported module.
\schemedisplay
(module m (y) (define y 'm-y))
(let ([x 'local-x] [y 'local-y])
(import-only m)
x) ;=> Error: x is not visible
\endschemedisplay
\noindent
This is sometimes desirable for static verification that no
identifiers are used except those explicitly imported into a
module or local scope.
Unless a module imported via \scheme{import-only} exports
\scheme{import} or
\scheme{import-only} and the name of at least one module, subsequent
imports within the scope of the \scheme{import-only} form are not
possible.
To create an isolated scope containing the exports of more than one
module without making \scheme{import} or \scheme{import-only}
visible, all of the modules to be imported must be listed in the
same \scheme{import-only} form.
Another solution is to create a single module that contains
the exports of each of the other modules.
\schemedisplay
(module m2 (y) (define y 'y))
(module m1 (x) (define x 'x))
(module mega-module (cons x y)
(import m1)
(import m2)
(import scheme))
(let ([y 3])
(import-only mega-module)
(cons x y)) ;=> (x . y)
\endschemedisplay
\bigskip
Before it is compiled, a source program is translated into
a core language program containing no syntactic abstractions, syntactic
definitions, library definitions, module definitions, or import forms.
Translation is performed by a \emph{syntax expander} that
processes the forms in the source program via recursive descent.
A \scheme{define-syntax} form associates a keyword
with a transformer in a translation-time environment.
When the expander encounters a keyword, it invokes the
associated transformer and reprocesses the resulting form.
A \scheme{module} form associates a module name with an interface.
When the expander encounters an \scheme{import} form, it extracts the
corresponding module interface from the translation-time environment and makes
the exported bindings visible in the scope where the \scheme{import} form
appears.
Internal definitions and definitions within a \scheme{module}
body are processed from left to right so that a module's definition
and import may appear within the same sequence of definitions.
Expressions appearing within a body and the right-hand sides of variable
definitions, however, are translated
only after the entire set of definitions has been processed, allowing
full mutual recursion among variable and syntactic definitions.
Module and import forms affect only the visibility of identifiers in
the source program, not their meanings.
In particular, variables are bound to locations whether defined within or
outside of a module, and \scheme{import} does not introduce new locations.
Local variables are renamed as necessary to preserve the scoping
relationships established by both modules and syntactic abstractions.
Thus, the expression:
\schemedisplay
(let ([x 1])
(module m (x setter)
(define-syntax x (identifier-syntax z))
(define setter (lambda (x) (set! z x)))
(define z 5))
(let ([y x] [z 0])
(import m)
(setter 3)
(+ x y z))) ;=> 4
\endschemedisplay
is equivalent to the following program
in which identifiers have been consistently renamed as indicated by
subscripts.
\schemedisplay
(let ([x\var{_0} 1])
(define-syntax x\var{_1} (identifier-syntax z\var{_1}))
(define setter\var{_1} (lambda (x\var{_2}) (set! z\var{_1} x\var{_2})))
(define z\var{_1} 5)
(let ([y\var{_3} x\var{_0}] [z\var{_3} 0])
(setter\var{_1} 3)
(+ x\var{_1} y\var{_3} z\var{_3})))
\endschemedisplay
Definitions within a top-level \scheme{begin}, \scheme{lambda}, top-level program,
\scheme{library}, or \scheme{module} body
are processed from left to right by the expander at expand time, and the
variable definitions are evaluated from left-to-right at run time.
Initialization expressions appearing within a \scheme{module} body
are evaluated in sequence after the evaluation of the variable
definitions.
Mutually recursive modules can be defined in several ways.
In the following program, \scheme{a} and \scheme{b} are mutually recursive
modules exported by an anonymous module whose local scope is used to
statically link the two.
For example,