-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsrfi-99.html
1446 lines (1233 loc) · 47 KB
/
srfi-99.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>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/srfi.css" type="text/css" />
<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 ``final'' 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 <em>final</em> status. Here is <a href="https://srfi.schemers.org/srfi-process.html">an explanation</a> of each status that a SRFI can hold. To provide input on this SRFI, please send email to <code><a href="mailto:srfi+minus+99+at+srfi+dotschemers+dot+org">srfi-99@<span class="antispam">nospam</span>srfi.schemers.org</a></code>. To subscribe to the list, follow <a href="https://srfi.schemers.org/srfi-list-subscribe.html">these instructions</a>. You can access previous messages via the mailing list <a href="https://srfi-email.schemers.org/srfi-99">archive</a>.</p>
<ul>
<li>Received: <a href="https://srfi.schemers.org/srfi-99/srfi-99-1.6.html">2008-06-03</a></li>
<li>Draft: 2008-07-22--2008-09-22</li>
<li>Revised: <a href="https://srfi.schemers.org/srfi-99/srfi-99-1.9.html">2009-08-28</a></li>
<li>Draft extended: 2009-08-28--2009-09-28</li>
<li>Revised: 2009-10-07</li>
</ul>
<h1>Table of contents</h1>
<ul>
<li><a href="#Abstract">Abstract</a></li>
<!--
<li><a href="#RevisionHistory">Revision History</a></li>
-->
<li><a href="#Rationale">Rationale</a></li>
<ul>
<li><a href="#History">Records Before R6RS</a></li>
<li><a href="#R6RSrecords">R6RS Records</a></li>
<li><a href="#DesignRationale">Design Rationale for ERR5RS Records</a></li>
<li><a href="#Issues">Issues</a></li>
</ul>
<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="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-1">[1]</a></sup>
</p>
<p>
The ERR5RS record system described by this SRFI provides
a simpler and fully portable alternative to the R6RS record
system.
The ERR5RS record system consists of
</p>
<div>
<ul>
<li>a syntactic layer, which is fully compatible with SRFI 9
but extends SRFI 9 with inheritance and with more succinct
syntax for common use cases</li>
<li>a procedural layer, which is simpler and easier to use
than the procedural layer of R6RS records but remains
fully compatible with the R6RS procedural layer</li>
<li>an inspection layer, which is simpler and easier to use
than the inspection layer of R6RS records but remains
fully compatible with the R6RS inspection layer</li>
</ul>
</div>
<p>
The ERR5RS record system does not mandate support for the
non-generative, sealed, and/or opaque features of the R6RS
record system.
Implementations of this SRFI may extend the ERR5RS record
system to support those features, however, and this SRFI
recommends an API to implementations that support those
features.
With those extensions, the ERR5RS record system has the
same expressive power as the R6RS record system. Hence
the record system described by this SRFI can serve as
either or both of the following:
</p>
<div>
<ul>
<li>an efficient and relatively simple foundation for
implementing the R6RS record system</li>
<li>a simpler and fully portable alternative to the R6RS
record system</li>
</ul>
</div>
<p>
<!--
To improve upon this unsatisfactory outcome, we should
first understand how it happened.
-->
The following subsections develop the rationale for this
SRFI by considering
</p>
<div>
<ul>
<li>the history of records in Scheme before the R6RS</li>
<li>R6RS records</li>
<li>the design rationale for ERR5RS records</li>
</ul>
</div>
<h2><a name="History">Records Before R6RS</a></h2>
<p>
The importance of adding records to Scheme has been
recognized for more than twenty years.
<sup><a href="#note-2">[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-3">[3]</a></sup>
Jonathan Rees posted a revision of Adams's proposal on
26 May 1988.
<sup><a href="#note-4">[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-5">[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-6">[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-7">[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-8">[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. He also argued that procedural
interfaces are difficult to compile efficiently, and
that this inefficiency would create portability problems.
<sup><a href="#note-9">[9]</a></sup>
</p>
<p>
In reality, however, procedural interfaces add no inefficiency.
It is now agreed that syntactic interfaces offer no
advantages for generative records.
<sup><a href="#note-10">[10]</a></sup>
Even for non-generative records, the claimed inefficiency
consists of a single load instruction, which optimizing
compilers can eliminate---along with the entire runtime check
that includes the load instruction---for all but the first of
a sequence of operations that access the same record. (That
optimization is a straightforward extension of the optimization
that eliminates the pair check when computing the <code>cdr</code>
of a list whose <code>car</code> has already been computed.)
Furthermore, it turns out that
even the occasional load instruction is no harder to remove
using a procedural interface than when using a syntactic
interface.
<sup><a href="#note-11">[11]</a></sup>
In R6RS library chapter 6, therefore, both of the statements that
claim an advantage in efficiency for the syntactic layer have no
basis in fact. (These two statements appear in the next-to-last
paragraph before section 6.1, and in the note that follows
the specification of <code>parent-rtd</code>.)
</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-12">[12]</a></sup>
Two days later, Dybvig proposed
a compromise along those lines
<sup><a href="#note-13">[13]</a></sup>
that incorporated several artificial restrictions,
which were apparently motivated by concerns about
the alleged extra load instruction.
<sup><a href="#note-14">[14]</a></sup>
Dybvig and Rozas continued to develop this proposal, and
presented a summary of it following the 1998 Scheme Workshop.
<sup><a href="#note-15">[15]</a></sup>
I have been unable to locate a written or online copy of
this proposal.
</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-16">[16]</a></sup>
</p>
<p>
Single inheritance was added by Larceny in 1998, and by
Chez Scheme in 1999.
<sup><a href="#note-17">[17]</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-18">[18]</a></sup>
</p>
<h2><a name="R6RSrecords">R6RS Records</a></h2>
<p>
The R6RS proposes a three-layer single inheritance system,
with syntactic, procedural, and inspection layers.
<sup><a href="#note-19">[19]</a></sup>
</p>
<h3><a name="R6RSprocedural">R6RS Records: procedural layer</a></h3>
<p>
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>
The (unratified) R6RS rationale
<sup><a href="#note-20">[20]</a></sup>
describes the
constructor-descriptor mechanism as "an infrastructure
for creating specialized constructors, rather than just
creating default constructors that accept the initial
values of all the fields as arguments.
This infrastructure achieves full generality while
leaving each level of an inheritance hierarchy in
control over its own fields and allowing child record
definitions to be abstracted away from the actual
number and contents of parent fields."
Neither the (ratified) R6RS library document nor the
(unratified) R6RS rationale consider the fact that the
constructor-descriptor mechanism adds unnecessary
complexity to what is by far the most common case:
record definitions that do not require specialized
constructors. Neither document considers the fact
that the benefits of the constructor-descriptor
mechanism are small even when specialized constructors
are needed, as in the
<a href="#Example1">first example below</a>.
</p>
<p>
The R6RS library specification of records says that
a record type is "specified by a record-type descriptor,
which is an object that specifies the fields of the record
and various other properties that all records of that type
share."
<sup><a href="#note-21">[21]</a></sup>
Since the record-type descriptor is an object, it can be
the value of a variable that is exported by a library.
As discussed below, however, the R6RS syntactic layer uses
a different notion of record type that may be neither object
nor syntax.
</p>
<h3><a name="R6RSsyntactic">R6RS Records: syntactic layer</a></h3>
<p>
The R6RS syntactic layer consists of a
<code>define-record-type</code> syntax
that is incompatible with the syntaxes of the same name
defined by SRFI 9 and SRFI 99 (this SRFI).
</p>
<p>
According to R6RS library section 6.2, an R6RS
<code>define-record-type</code> form binds the record name
"to an expand-time or run-time representation of the record
type [that] can be used as parent name in syntactic
record-type definitions that extend this definition.
It can also be used as a handle to gain access to the
underlying record-type descriptor and constructor
descriptor".
</p>
<p>
Note that portable code cannot assume the record name is
bound to a record-type descriptor. Portable code can only
assume that the record name is bound to "an expand-time
or run-time representation", whose semantics is not otherwise explained
by the R6RS and R6RS library documents. In particular,
it is far from obvious that portable code can export the
record name from a library; libraries can export names that
are bound to objects or to syntax, but the R6RS does not
require the denotation of a record name to be
either of those things.
</p>
<p>
The mysterious entity to which a record name is bound can
be used as a handle to recover a record-type descriptor or
constructor descriptor by using the
<code>record-type-descriptor</code> or
<code>record-constructor-descriptor</code> syntaxes,
respectively. The recovered record-type descriptor and
constructor descriptor may be exported from a library,
and that is apparently the only portable way for a library
to export an R6RS record type that was defined using the
R6RS syntactic layer.
</p>
<p>
The recovered record-type descriptor and constructor
descriptor also provide a way for the procedural layer
to define new record types that inherit from record
types defined by the syntactic layer.
Similarly, it is possible for the syntactic layer to
use a <code>parent-rtd</code> clause to define new
record types that inherit from record types defined
by the procedural layer.
</p>
<p>
The two notions of record type that are used by the
procedural and syntactic layers are not interchangeable,
however. In either direction, defining a new record type
that inherits from some previously defined record type
requires the programmer to know whether the previously
defined record type was defined using the procedural or
the syntactic layer.
If the procedural and syntactic layers of the R6RS
were fully compatible, then changing a record type
definition from procedural to syntactic (or vice versa)
would be transparent to clients. As the R6RS record
facility is defined, however, that minor change will
break all code that inherits from the record type.
</p>
<p>
R6RS library chapter 6 attempts to excuse that incompatibility,
and the interoperability and maintenance problems that
result from it, on the basis of efficiency.
Recall, however, that the claimed efficiency of the R6RS
syntactic layer is illusory. In reality, the R6RS design
offers no advantages over a simpler and more orthogonal
design (such as the one specified by this SRFI) in which
the syntactic and procedural layers both use the same
notion of record type.
</p>
<p>
The problems described above were known and had been
documented before the R6RS documents were put to a vote,
but the R6RS documents were ratified anyway.
<sup><a href="#note-22">[22]</a></sup>
At this point, the best that can be done is to use the
SRFI process to specify a better record facility, and
to warn programmers of the problems they will encounter
if they use the record facilities described within the
R6RS library document.
</p>
<h2><a name="DesignRationale">Design Rationale for ERR5RS Records</a></h2>
<p>
The ERR5RS syntactic layer described by this SRFI
is 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 <code>define-record-type</code> syntax specified by
this SRFI is compatible with and extends SRFI 9, which
is one of the more widely accepted SRFIs.
The extensions include single inheritance and (optional)
implicit naming, along with succinct abbreviations for
specifying whether a field is immutable or mutable.
</p>
<p>
The procedural layer specified by this SRFI is fully
compatible with its <code>define-record-type</code> syntax.
Both the procedural and syntactic layers can define new
record types that inherit from previously defined record
types without requiring programmers to know which layer
was used to define the parent type.
</p>
<p>
In implementations of the R6RS, a SRFI 99 record type
coincides with the R6RS notion of a record-type descriptor.
Portable libraries can safely export SRFI 99 record types
even if they were defined using the syntactic layer of
SRFI 99.
</p>
<p>
In procedure names, SRFI 99 uses <code>rtd</code> as
an abbreviation for record-type descriptor. This naming
convention prevents name clashes between SRFI 99 and the
R6RS procedural and inspection layers, which makes it
easier for R6RS programs to import SRFI 99 libraries.
R6RS programs must take care when importing the R6RS
syntactic layer, however, because that library's exports
conflict with both SRFI 9 and with SRFI 99.
</p>
<p>
When implemented properly, SRFI 99 records will be just
as efficient as R6RS records. SRFI 99 is simpler than
R6RS records, both in specification and in implementation.
SRFI 99 is strictly less powerful than the R6RS records
facility because SRFI 99 does not require implementations
to provide sealed, opaque, or non-generative records.
On the other hand, SRFI 99 describes three optional
extensions (the <code>sealed</code>, <code>opaque</code>,
and <code>uid</code> arguments to <code>make-rtd</code>)
that would give SRFI 99 the same power as R6RS records.
With those three extensions, SRFI 99 would become a simple
and efficient foundation for implementing R6RS records.
</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>
<!--
<h2><a name="ERR5RSgeneral">Relationship to ERR5RS</a></h2>
<p>
FIXME
</p>
-->
<h2><a name="Issues">Issues</a></h2>
<div>
<ul>
<li>ERR5RS records support single inheritance.</li>
<li>ERR5RS records do not support the non-generative
feature of R6RS records.
(To show how SRFI 99 could be extended to provide
all useful features of R6RS records,
SRFI 99 describes an optional extension for
non-generativity, and the reference implementation shows
how that extension is implemented in Larceny, but
that extension is not part of SRFI 99's API.)
</li>
<li>ERR5RS records do not support the optional "sealed"
feature of R6RS records.
(To show how SRFI 99 could be extended to provide
all useful features of R6RS records,
SRFI 99 describes an optional extension for
sealed records, and the reference implementation shows
how that extension is implemented in Larceny, but
that extension is not part of SRFI 99's API.)
</li>
<li>ERR5RS records do not support the optional "opaque"
feature of R6RS records.
(To show how SRFI 99 could be extended to provide
all useful features of R6RS records,
SRFI 99 describes an optional extension for
opaque records, and the reference implementation shows
how that extension is implemented in Larceny, but
that extension is not part of SRFI 99's API.)
</li>
<li>ERR5RS records do not support the record-constructor
descriptors of R6RS records.
(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>ERR5RS records support the per-field
mutable/immutable feature of R6RS records.
</li>
<li>The <code>define-record-type</code> syntax provides
succinct defaults for both immutable and mutable fields.
An identifier-only field spec implies immutability.
When enclosed within parentheses, a single identifier
implies mutability.
</li>
<li>ERR5RS records are partitioned into a procedural layer,
an inspection layer, and a syntactic layer.
(The primary reason for separating the procedural layer
from the inspection layer is to simplify comparisons
with R6RS records.)
</li>
<li>The <code>make-rtd</code> procedure accepts a vector of
field specifiers instead of a list of field specifiers.
(This is carried over from the corresponding procedure
of the R6RS procedural layer.)
</li>
</ul>
</div>
<h1><a name="Specification">Specification</a></h1>
<p>
All implementations of SRFI 99 must provide the
following libraries:
</p>
<pre>
(srfi :99) ; alias for (srfi :99 records)
(srfi :99 records) ; composite of the next three
(srfi :99 records procedural)
(srfi :99 records inspection)
(srfi :99 records syntactic)
</pre>
<p>
Implementations of ERR5RS should provide the following aliases
as well:
</p>
<pre>
(err5rs records) ; alias for (srfi :99 records)
(err5rs records procedural) ; alias for (srfi :99 records procedural)
(err5rs records inspection) ; alias for (srfi :99 records inspection)
(err5rs records syntactic) ; alias for (srfi :99 records syntactic)
</pre>
<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.
Neither ERR5RS nor this SRFI mandate the R6RS exception semantics
for programs that violate the specification.
</p>
<h2>
<a name="ProceduralLayer">Procedural Layer</a>
</h2>
<p>
The <code>(srfi :99 records procedural)</code> library
exports the following procedures.
</p>
<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>
The recommendation above is not binding on implementations of SRFI 99.
There are other ways to realize sealed, opaque, or non-generative rtds.
</p>
<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 (unspecified)))
(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 (rtd-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>
The <code>(srfi :99 records inspection)</code> library
exports the following procedures.
</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.