-
Notifications
You must be signed in to change notification settings - Fork 0
/
srfi-99-1.6.html
1477 lines (1241 loc) · 47.1 KB
/
srfi-99-1.6.html
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
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- !DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html40/loose.dtd" -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SRFI 99: ERR5RS Records</title>
<style type="text/css">
.add { color: red; }
.del { color: red; text-decoration: line-through; }
.sub { vertical-align: sub; text-size: tiny; }
</style>
</head>
<body>
<!-- This commented out text is for the brittle SRFI tools -->
<!--
<H1>Title</H1>
ERR5RS Records
<H1>Author</H1>
William D Clinger
<H1>Status</H1>
This SRFI is currently in ``draft'' status.
-->
<!-- This is the real, valid XHTML text -->
<h1>Title</h1>
<p>ERR5RS Records</p>
<h1>Author</h1>
<p>William D Clinger</p>
<h1>Status</h1>
<p>
This SRFI is currently in ``draft'' status. To see an explanation of
each status that a SRFI can hold, see <a
href="http://srfi.schemers.org/srfi-process.html">here</a>.
To provide input on this SRFI, please
<a href="mailto:srfi-99 at srfi dot schemers dot org">mail to
<code><srfi minus 99 at srfi dot schemers dot org></code></a>. See
<a href="../../srfi-list-subscribe.html">instructions here</a> to subscribe to
the list. You can access previous messages via
<a href="mail-archive/maillist.html">the archive of the mailing list</a>.
</p>
<ul>
<li>Received: 2008/06/03</li>
<li>Draft: 2008/07/22-2008/09/22</li>
</ul>
<h1>Table of contents</h1>
<ul>
<li><a href="#Abstract">Abstract</a></li>
<li><a href="#Issues">Issues</a></li>
<li><a href="#RevisionHistory">Revision History</a></li>
<li><a href="#Rationale">Rationale</a></li>
<li><a href="#Specification">Specification</a>
<ul>
<li><a href="#ProceduralLayer">Procedural Layer</a></li>
<li><a href="#InspectionLayer">Inspection Layer</a></li>
<li><a href="#SyntacticLayer">Syntactic Layer</a></li>
<li><a href="#RecordIdentity">Record Identity</a></li>
</ul>
</li>
<li><a href="#Examples">Examples</a></li>
<li><a href="#ReferenceImplementation">Reference Implementation</a></li>
<li><a href="#References">References</a></li>
<li><a href="#Acknowledgements">Acknowledgements</a></li>
<li><a href="#Copyright">Copyright</a></li>
</ul>
<h1><a name="Abstract">Abstract</a></h1>
<p>
Many Scheme programmers have considered records to be one
of the most important features missing from the R5RS.
The R6RS proposed a record system, but its design has
been widely criticized and it was not intended for use
in R5RS programs anyway.
</p>
<p>
This SRFI proposes a better record system for use in R5RS,
ERR5RS, and R6RS programs. The syntactic layer of this
SRFI's record system is an extension of SRFI 9. The
procedural and inspection layers of this SRFI's record
system are perfectly compatible with its syntactic layer.
This entire SRFI is compatible with the procedural and
inspection layers of the R6RS record system, but offers
several worthwhile improvements over the R6RS system.
</p>
<h1><a name="Issues">Issues</a></h1>
<p>
This SRFI's proposed answers to the following questions are
indicated within parentheses.
</p>
<div>
<ul>
<li>Should ERR5RS records support single inheritance?<br />
(Yes.)
</li>
<li>Should ERR5RS records support the non-generative
feature of R6RS records?<br />
(No, but this SRFI describes an optional extension for
non-generativity, and the reference implementation shows
how that extension is implemented in Larceny.)
</li>
<li>Should ERR5RS records support the optional "sealed"
feature of R6RS records?<br />
(No, but this SRFI describes an optional extension for
sealed records, and the reference implementation shows
how that extension is implemented in Larceny.)
</li>
<li>Should ERR5RS records support the optional "opaque"
feature of R6RS records?<br />
(No, but this SRFI describes an optional extension for
opaque records, and the reference implementation shows
how that extension is implemented in Larceny.)
</li>
<li>Should ERR5RS records support the per-field
mutable/immutable feature of R6RS records?<br />
(Yes.)
</li>
<li>Should ERR5RS support the record-constructor
descriptors of R6RS records?<br />
(No. Even so, ERR5RS
records are compatible with the
<code>make-record-constructor-descriptor</code>
procedure of the R6RS. Larceny, for example, allows that
procedure to be used with ERR5RS record type descriptors.)
</li>
<li>Should mutable or immutable be the default for
fields that specify neither property explicitly?<br />
(For the <code>make-rtd</code> procedure, the succinct
identifier-only field spec implies mutability.
The <code>define-record-type</code> syntax provides
succinct abbreviations for both immutable and mutable
fields.)
</li>
</ul>
</div>
<h1><a name="RevisionHistory">Revision History</a></h1>
<p>
This draft has not yet been submitted to the SRFI editors.
</p>
<h1><a name="Rationale">Rationale</a></h1>
<p>
In most programming languages, records (aka structures
or classes) are important because they can package
component values of different types into a single object.
</p><p>Scheme's vectors and procedures provided that capability
already, but records remained important for two reasons:
</p>
<div>
<ul><li>Records make it easier to index components by their names.
</li>
<li>Records can introduce new types in the sense that all
previous type predicates return false for instances of
the new record type, while the new predicate associated
with the new record type returns true only for instances
of the new record type.
</li>
</ul>
</div>
<p>
For many programmers, records were the most important
new feature of the R6RS, but the specific record systems
that were proposed by the R6RS have been widely criticized.
Over 30% of those who voted against ratification mentioned
the record systems as one of their reasons.
<sup><a href="#note-0">[1]</a></sup>
</p>
<p>
To improve upon this unsatisfactory outcome, we should
first understand how it happened.
</p>
<p>
The importance of adding records to Scheme has been
recognized for more than twenty years.
<sup><a href="#note-1">[2]</a></sup>
The basic idea behind the SRFI 9 and R6RS record systems
was outlined by Norman Adams on 8 July 1987, following
similar ideas that had been implemented in T and MIT CScheme.
<sup><a href="#note-2">[3]</a></sup>
Jonathan Rees posted a revision of Adams's proposal on
26 May 1988.
<sup><a href="#note-3">[4]</a></sup>
Pavel Curtis proposed an extension of Rees's proposal
on 18 August 1989, noting that it had been approved by
consensus at the first meeting of BASH (Bay Area Scheme
Hackers?).
<sup><a href="#note-4">[5]</a></sup>
The <code>rrrs-authors</code> archive includes several responses to
these proposals that are worth reading.
</p>
<p>
The Rees/Curtis proposal was revived in 1992.
<sup><a href="#note-5">[6]</a></sup>
When the RnRS authors met on 25 June 1992 in Palo Alto,
they felt that this proposal needed more discussion.
<sup><a href="#note-6">[7]</a></sup>
Kent Dybvig objected to the proposal on several grounds,
including the provision of inspection facilities, the
inability to define immutable records, and the use of
procedures instead of special forms. Although 9 authors
favored adoption of the records proposal, 11 opposed it.
</p>
<p>
The topic of records was revived again on 23 April 1996 by Bruce
Duba, Matthew Flatt, and Shriram Krishnamurthi.
<sup><a href="#note-7">[8]</a></sup>
Alan Bawden
and Richard Kelsey observed that the Duba/Flatt/Krishnamurthi
proposal was essentially the same as Pavel Curtis's, which
Kelsey reposted. Kent Dybvig objected once again, on the
same three grounds, arguing (incorrectly) that procedural
interfaces are difficult to compile efficiently, and
concluding (incorrectly) that this alleged inefficiency
would create portability problems.
<sup><a href="#note-8">[9]</a></sup>
</p>
<p>
On 24 April 1996, Bill Rozas suggested the idea of having
two separate APIs, one procedural and one syntactic, for
the same record facility.
<sup><a href="#note-9">[10]</a></sup>
Two days later, Dybvig proposed
a "compromise" along those lines
<sup><a href="#note-10">[11]</a></sup>
that incorporated several artificial restrictions,
apparently because Dybvig feared his compiler
would be unable to generate efficient code for the general
case.
<sup><a href="#note-11">[12]</a></sup>
</p>
<p>
SRFI 9, submitted by Richard Kelsey in July 1999, is a
syntactic API in the tradition of the Rees, Curtis, and
Duba/Flatt/Krishnamurthi proposals.
<sup><a href="#note-12">[13]</a></sup>
</p>
<p>
Single inheritance was added by Larceny in 1998, and by
Chez Scheme in 1999.
<sup><a href="#note-13">[14]</a></sup>
</p>
<p>
SRFI 57, submitted by Andre van Tonder in September 2004,
features label polymorphism, which can be considered a
form of structural subtyping and multiple inheritance.
<sup><a href="#note-14">[15]</a></sup>
</p>
<p>
The R6RS proposes a three-layer single inheritance system,
with syntactic, procedural, and inspection layers.
<sup><a href="#note-15">[16]</a></sup>
The R6RS procedural layer generally requires at least three
separate definitions for each level of inheritance: the
record-type descriptor, at least one record-constructor
descriptor, and an actual constructor (if instances of
the record-type are to be created).
</p>
<p>
Some attempts have been made to justify the complexity of
the R6RS procedural layer by claiming that hardly anyone
would actually use it;
programmers who try to use the procedural layer would
presumably give up in frustration and use the syntactic
interface instead.
Meanwhile, gratuitous design errors
impede interoperation between procedural and syntactic
layers, and several bogus claims about the inefficiency
of higher order procedures were inserted into the very
last public draft and were then ratified as part of the R6RS.
<sup><a href="#note-16">[17]</a></sup>
</p>
<p>
When it comes to records, there is no good reason for
ERR5RS records or other SRFIs to strive for full
bug-compatibility with the R6RS. Fortunately, it
is possible to design good APIs for records that
interoperate well with the R6RS's procedural and
inspection layers, even though the better APIs may differ
from the R6RS APIs in several important respects.
</p>
<p>
It is not possible to design APIs that interoperate
well with both the R6RS syntactic and procedural layers,
because the R6RS syntactic layer uses a fundamentally
different notion of record type than the R6RS procedural
layer.
On the other hand, it is not hard to design a syntactic
API that interoperates with the R6RS APIs at least as well
as the R6RS APIs interoperate with each other.
</p>
<p>
The ERR5RS syntactic layer is therefore based upon the
Rees/Curtis/Duba/Flatt/Krishnamurthi/Kelsey/SRFI-9
tradition, changing only a few details to improve
interoperability with records defined by the ERR5RS
and R6RS procedural layers.
</p>
<p>
The record system described by this SRFI has been
implemented in Larceny. It is the primary record
system used by Larceny's implementation of the R6RS,
including the <code>(rnrs records syntactic)</code>
library. Larceny demonstrates both the efficiency
of ERR5RS records and the ease of interoperability
between SRFI 9, ERR5RS, and the procedural and
inspection layers of R6RS records.
</p>
<h1><a name="Specification">Specification</a></h1>
<p>
The specification is divided into three layers,
which correspond to the following standard libraries
of ERR5RS:
</p>
<div>
<ul>
<li><code>(err5rs records procedural)</code></li>
<li><code>(err5rs records inspection)</code></li>
<li><code>(err5rs records syntactic)</code></li>
</ul>
</div>
<p>
The specification also describes how Scheme's standard
equivalence predicates behave with respect to records,
and shows how some R6RS examples can be translated to
use the ERR5RS libraries instead.
</p>
<p>
When the following specification says that a procedure
is said to be equivalent to some R6RS procedure, the
equivalence holds only when all arguments have the
properties required of them by the R6RS specification.
ERR5RS does not mandate the R6RS exception semantics
for programs that violate the specification.
</p>
<h2>
<a name="ProceduralLayer">Procedural Layer</a>
</h2>
<p><code>(make-rtd <i>name</i> <i>fieldspecs</i>)</code></p>
<p><code>(make-rtd <i>name</i> <i>fieldspecs</i> <i>parent</i>)</code></p>
<p>
<i>name</i> is a symbol, which matters only to the
<code>rtd-name</code> procedure of the inspection layer.
<i>fieldspecs</i> is a vector of field specifiers, where
each field specifier is one of
</p>
<div>
<ul>
<li>a symbol naming the (mutable) field;</li>
<li>a list of the form <code>(mutable <i>name</i>)</code>,
where <i>name</i> is a symbol naming the mutable field;</li>
<li>a list of the form <code>(immutable <i>name</i>)</code>,
where <i>name</i> is a symbol naming the immutable field.</li>
</ul>
</div>
<p>
The optional <i>parent</i> is an rtd or <code>#f</code>.
It is an error for any of the symbols in <i>fieldspecs</i> to
name more than one of the fields specified by <i>fieldspecs</i>,
but the field names in <i>fieldspecs</i> may shadow field names
in the <i>parent</i> record-type.
</p>
<p>
Implementations may wish to extend this procedure to support
the non-generative, sealed, and/or opaque features of the R6RS.
The recommended way to support those features is to allow any
combination of the following arguments to follow the optional
<i>parent</i> argument:
</p>
<div>
<ul>
<li>The symbol <code>sealed</code> means the new rtd cannot
be used as the parent of other rtds.</li>
<li>The symbol <code>opaque</code> means the <code>record?</code>
predicate will not recognize instances of the new rtd.</li>
<li>The symbol <code>uid</code>, following by another symbol
<i>id</i>, means the new rtd is non-generative with uid <i>id</i>.
The semantics of this extension is the same as described by the
R6RS.</li>
</ul>
</div>
<p>
Returns an R6RS-compatible record-type descriptor.
Could be defined (without the recommended error
checking, and without the extensions described above)
in terms of the R6RS procedural layer by
</p>
<div>
<pre>
(define (make-rtd name fieldspecs . rest)
(make-record-type-descriptor
name
(if (null? rest) #f (car rest))
#f #f #f
(vector-map (lambda (fieldspec)
(if (symbol? fieldspec)
(list 'mutable fieldspec)
fieldspec))
fieldspecs)))
</pre>
</div>
<p><code>(rtd? <i>obj</i>)</code></p>
<p>
Equivalent to the <code>record-type-descriptor?</code>
procedure of the R6RS.
</p>
<p><code>(rtd-constructor <i>rtd</i>)</code></p>
<p><code>(rtd-constructor <i>rtd</i> <i>fieldspecs</i>)</code></p>
<p>
<i>rtd</i> is a record-type descriptor, and <i>fieldspecs</i>
is an optional vector of symbols.
</p>
<p>
If no <i>fieldspecs</i> argument is supplied, then
<code>rtd-constructor</code> returns a procedure
that expects one argument for each field of the
record-type described by <i>rtd</i> and returns an
instance of that record-type with its fields
initialized to the corresponding arguments.
Arguments that correspond to the fields of the
record-type's parent (if any) come first.
</p><p>If <i>fieldspecs</i> is supplied, then
<code>rtd-constructor</code> returns a procedure
that expects one argument for each element of
<i>fieldspecs</i> and returns an instance of the
record-type described by <i>rtd</i> with the named
fields initialized to the corresponding arguments.
</p><p>It is an error if some symbol occurs more than once
in <i>fieldspecs</i>. Fields of a derived record-type
shadow fields of the same name in its parent; the
<i>fieldspecs</i> argument cannot be used to initialize
a shadowed field.
</p>
<blockquote>
<p>
<em>Note:</em>
The optional second argument was proposed by Pavel Curtis,
and interoperates well with SRFI 9.
</p>
</blockquote>
<p>
Could be defined in terms of the R6RS procedural layer and
ERR5RS inspection layer by:
</p>
<div>
<pre>
(define (rtd-constructor rtd . rest)
; Computes permutation and allocates permutation buffer
; when the constructor is created, not when the constructor
; is called. More error checking is recommended.
(define (make-constructor fieldspecs allnames maker)
(let* ((k (length fieldspecs))
(n (length allnames))
(buffer (make-vector n 'some-unspecified-value))
(reverse-all-names (reverse allnames)))
(define (position fieldname)
(let ((names (memq fieldname reverse-all-names)))
(assert names)
(- (length names) 1)))
(let ((indexes (map position fieldspecs)))
; The following can be made quite efficient by
; hand-coding it in some lower-level language,
; e.g. Larceny's mal. Even case-lambda would
; be good enough in most systems.
(lambda args
(assert (= (length args) k))
(for-each (lambda (arg posn)
(vector-set! buffer posn arg))
args indexes)
(apply maker (vector->list buffer))))))
(if (null? rest)
(record-constructor
(make-record-constructor-descriptor rtd #f #f))
(begin (assert (null? (cdr rest)))
(make-constructor
(vector->list (car rest))
(vector->list (record-type-all-field-names rtd))
(record-constructor
(make-record-constructor-descriptor rtd #f #f))))))
</pre>
</div>
<p><code>(rtd-predicate <i>rtd</i>)</code></p>
<p>
Equivalent to the <code>record-predicate</code> procedure of the R6RS.
</p>
<p><code>(rtd-accessor <i>rtd</i> <i>field</i>)</code></p>
<p>
<i>field</i> is a symbol that names a field of the record-type
described by the record-type descriptor <i>rtd</i>. Returns a
unary procedure that accepts instances of <i>rtd</i> (or any
record-type that inherits from <i>rtd</i>) and returns the
current value of the named field.
</p>
<p>
Fields in derived record-types shadow fields of the same
name in a parent record-type.
</p>
<p><code>(rtd-mutator <i>rtd</i> <i>field</i>)</code></p>
<p>
<i>field</i> is a symbol that names a field of the record-type
described by the record-type descriptor <i>rtd</i>. Returns a
binary procedure that accepts instances of <i>rtd</i> (or any
record-type that inherits from <i>rtd</i>) and a new value to
be stored into the named field, performs that side effect,
and returns an unspecified value.
</p>
<p>
Fields in derived record-types shadow fields of the same
name in a parent record-type.
</p>
<hr />
<h2>
<a name="InspectionLayer">Inspection Layer</a>
</h2>
<p>
When a procedure is said to be equivalent to an
R6RS procedure, the equivalence holds only when all
arguments have the properties required of them by
the R6RS specification. ERR5RS does not mandate
the R6RS exception semantics for programs that
violate the specification.
</p>
<p>
<code>(record? <i>obj</i>)</code>
</p>
<p>
Equivalent to its R6RS namesake.
</p>
<p>
<code>(record-rtd <i>record</i>)</code>
</p>
<p>
Equivalent to its R6RS namesake.
</p>
<p>
<code>(rtd-name <i>rtd</i>)</code>
</p>
<p>
Equivalent to the <code>record-type-name</code> procedure of the R6RS.
</p>
<p>
<code>(rtd-parent <i>rtd</i>)</code>
</p>
<p>
Equivalent to the <code>record-type-parent</code> procedure of the R6RS.
</p>
<p>
<code>(rtd-field-names <i>rtd</i>)</code>
</p>
<p>
Equivalent to the <code>record-type-field-names</code>
procedure of the R6RS. (That is, it returns
a vector of the symbols that name the fields of the
record-type represented by <i>rtd</i>, excluding
the fields of parent record-types.)
</p>
<p>
<code>(rtd-all-field-names <i>rtd</i>)</code>
</p>
<p>
Returns a vector of the symbols that name the fields
of the record-type represented by <i>rtd</i>, including
the fields of its parent record-types, if any.
The fields of parent record-types come before the
fields of its children, with each subsequence in the
same order as in the vectors that would be returned
by calling <code>rtd-field-names</code> on <i>rtd</i>
and on all its ancestral record-type descriptors.
</p>
<p>
Could be defined by
</p>
<pre>
(define (rtd-all-field-names rtd)
(define (loop rtd othernames)
(let ((parent (rtd-parent rtd))
(names (append (vector->list
(rtd-field-names rtd))
othernames)))
(if parent
(loop parent names)
(list->vector names))))
(loop rtd '()))
</pre>
<p><code>(rtd-field-mutable? <i>rtd</i> <i>field</i>)</code>
</p>
<p>
<i>rtd</i> is a record-type descriptor, and <i>field</i>
is a symbol naming a field of the record-type
described by <i>rtd</i>. Returns <code>#t</code>
if the named field is mutable; otherwise returns
<code>#f</code>.
</p>
<hr />
<h2>
<a name="SyntacticLayer">Syntactic Layer</a>
</h2>
<p>
The syntactic layer consists of SRFI 9 extended with
single inheritance and (optional) implicit naming.
</p>
<p>
All ERR5RS record-type definitions are generative,
but ERR5RS drops the SRFI 9 restriction to top level,
mainly because the R6RS allows generative definitions
wherever a definition may appear.
</p>
<p>
The syntax of an ERR5RS record-type definition is
</p>
<pre>
<definition>
-> <record type definition> ; addition to 7.1.6 in R5RS
<record type definition>
-> (define-record-type <type spec>
<constructor spec>
<predicate spec>
<field spec> ...)
<type spec> -> <type name>
-> (<type name> <parent>)
<constructor spec>
-> #f
-> #t
-> <constructor name>
-> (<constructor name> <field name> ...)
<predicate spec>
-> #f
-> #t
-> <predicate name>
<field spec> -> <field name>
-> (<field name>)
-> (<field name> <accessor name>)
-> (<field name> <accessor name> <mutator name>)
<parent> -> <expression>
<type name> -> <identifier>
<constructor name> -> <identifier>
<predicate name> -> <identifier>
<accessor name> -> <identifier>
<mutator name> -> <identifier>
<field name> -> <identifier>
</pre>
<p>
The semantics of a record type definition is the same
as in SRFI 9: the record type definition macro-expands
into a cluster of definitions that
</p>
<ul>
<li>define the <code><type name></code> as the
record-type descriptor for the new record-type;
</li>
<li>defines a constructor for instances of the new
record-type (unless the constructor spec is <code>#f</code>);
</li>
<li>defines a predicate that recognizes instances of the
new record-type and its subtypes (unless the predicate
spec is <code>#f</code>);
</li>
<li>defines an accessor for each field name;
</li>
<li>defines a mutator for each mutable field name.
</li>
</ul>
<p>
An ERR5RS record type definition extends SRFI 9 with the
following additional options:
</p>
<ul>
<li>If a <code><parent></code> expression is specified,
then it must evaluate to an rtd that serves as the parent
record-type for the record-type being defined.
</li>
<li>If <code>#f</code> is specified for the constructor or
predicate, then no constructor or predicate procedure is defined.
(This is useful when the record-type being defined will be used
as an abstract base class.)
</li>
<li>If <code>#t</code> is specified for the constructor or
predicate, then the name of the constructor is the type name
prefixed by <code>make-</code>, and the name of the predicate
is the type name followed by a question mark (<code>?</code>).
</li>
<li>If the constructor name is specified as <code>#t</code> or
as an identifier, then the constructor's arguments correspond
to the fields of the parent (if any) followed by the new fields
added by this record-type definition.
</li>
<li>If a field spec consists of a single identifier, then
<ul>
<li>the field is immutable;
</li>
<li>the name of its accessor is the type name followed by a
hyphen (<code>-</code>) followed by the field name.
</li>
</ul>
</li>
<li>If a field spec consists of a list of one identifier, then
<ul>
<li>the field is mutable;
</li>
<li>the name of its accessor is the type name followed by a
hyphen (<code>-</code>) followed by the field name;
</li>
<li>the name of its mutator is the type name followed by a
hyphen (<code>-</code>) followed by the field name
followed by <code>-set!</code>.
</li>
</ul>
</li>
</ul>
<hr />
<h2>
<a name="RecordIdentity">Record Identity</a>
</h2>
<p>Two ERR5RS records with fields are <code>eqv?</code>
if and only if they were created by the same (dynamic)
call to some record constructor. Two ERR5RS records
are <code>eq?</code> if and only if they are
<code>eqv?</code>. Two ERR5RS records
are <code>equal?</code> if and only if they are
<code>eqv?</code>.
</p>
<dl><dd>(Historical note: Pavel Curtis proposed that
<code>equal?</code> behave the same as <code>eqv?</code>.)
</dd></dl>
<p>
A <code>define-record-type</code> form macro-expands
into code that calls <code>make-rtd</code> each time
the expanded record-type definition is executed.
Two ERR5RS record-type descriptors are <code>eqv?</code>
if and only if they were created by the same (dynamic)
call to <code>make-rtd</code>.
</p>
<hr />
<h1>
<a name="Examples">Examples</a>
</h1>
<p>
<a href="http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-7.html#node_sec_6.3">R6RS library section 6.3</a>
includes two extended examples that provide a nice comparison of
the R6RS and ERR5RS record systems, especially since these two
examples were designed to highlight the use of R6RS
record-constructor descriptors in combination with inheritance.
</p>
<p>
Using ERR5RS records, the first example becomes:
</p>
<pre>
(define rtd1
(make-rtd 'rtd1 '#((immutable x1) (immutable x2))))
(define rtd2
(make-rtd 'rtd2 '#((immutable x3) (immutable x4)) rtd1))
(define rtd3
(make-rtd 'rtd3 '#((immutable x5) (immutable x6)) rtd2))
(define protocol1
(lambda (p)
(lambda (a b c)
(p (+ a b) (+ b c)))))
(define protocol2
(lambda (n)
(lambda (a b c d e f)
(let ((p (n a b c)))
(p (+ d e) (+ e f))))))
(define protocol3
(lambda (n)
(lambda (a b c d e f g h i)
(let ((p (n a b c d e f)))
(p (+ g h) (+ h i))))))
(define make-rtd1
(protocol1 (rtd-constructor rtd1)))
(define make-rtd2
(let ((maker2 (rtd-constructor rtd2)))
(protocol2
(protocol1
(lambda (x1 x2)
(lambda (x3 x4)
(maker2 x1 x2 x3 x4)))))))
(define make-rtd3
(let ((maker3 (rtd-constructor rtd3)))
(protocol3
(protocol2
(protocol1
(lambda (x1 x2)
(lambda (x3 x4)
(lambda (x5 x6)
(maker3 x1 x2 x3 x4 x5 x6)))))))))
(make-rtd3 1 2 3 4 5 6 7 8 9)
; evaluates to a record whose fields contain
; 3 5 9 11 15 17
</pre>
<p>
The purpose of the R6RS record-constructor descriptors is to
automate the idiom shown in the definitions of
<code>make-rtd1</code>, <code>make-rtd2</code>,
and <code>make-rtd3</code> above, and to provide an alternative
to procedural abstraction when eliminating the duplication of
code seen in <code>make-point/abs</code> and
<code>make-cpoint/abs</code> below.
</p>
<p>
The second example illustrates the shadowing of fields in a
parent record-type by fields in a derived record-type. Using
ERR5RS records, the second example becomes:
</p>
<pre>
(define :point
(make-rtd 'point '#((mutable x) (mutable y))))
(define make-point (rtd-constructor :point))
(define point? (rtd-predicate :point))
(define point-x (rtd-accessor :point 'x))
(define point-y (rtd-accessor :point 'y))
(define point-x-set! (rtd-mutator :point 'x))
(define point-y-set! (rtd-mutator :point 'y))
(define p1 (make-point 1 2))
(point? p1) => #t
(point-x p1) => 1
(point-y p1) => 2
(point-x-set! p1 5)
(point-x p1) => 5
(define :point2
(make-rtd 'point2 '#((mutable x) (mutable y)) :point))
(define make-point2
(rtd-constructor :point2))
(define point2? (rtd-predicate :point2))
(define point2-xx (rtd-accessor :point2 'x))
(define point2-yy (rtd-accessor :point2 'y))
(define p2 (make-point2 1 2 3 4))
(point? p2) => #t
(point-x p2) => 1
(point-y p2) => 2
(point2-xx p2) => 3
(point2-yy p2) => 4
(define make-point/abs
(let ((maker (rtd-constructor :point)))
(lambda (x y)
(maker (abs x) (abs y)))))
(point-x (make-point/abs -1 -2)) => 1
(point-y (make-point/abs -1 -2)) => 2
(define :cpoint
(make-rtd 'cpoint '#((mutable rgb)) :point))
(define make-cpoint
(let ((maker (rtd-constructor :cpoint)))
(lambda (x y c)
(maker x y (color->rgb c)))))