The define-record-type
form can be used for creating new data
types, called record types. A predicate, constructor, and field
accessors and modifiers are defined for each record type.
The define-record-type
feature is specified
by SRFI-9,
which is implemented by many modern Scheme implementations.
Syntax: define-record-type
(type-name
constructor-name
field-tag
...
)
(predicate-name
field-tag
[accessor-name
]) modifier-name
...
The form
define-record-type
is generative: each use creates a new record type that is distinct from all existing types, including other record types and Scheme’s predefined types. Record-type definitions may only occur at top-level (there are two possible semantics for ‘internal’ record-type definitions, generative and nongenerative, and no consensus as to which is better).An instance of
define-record-type
is equivalent to the following definitions:
The
type-name
is bound to a representation of the record type itself.The
constructor-name
is bound to a procedure that takes as many arguments as there arefield-tag
s in the(
subform and returns a newconstructor-name
...)type-name
record. Fields whose tags are listed withconstructor-name
have the corresponding argument as their initial value. The initial values of all other fields are unspecified.The
predicate-name
is a predicate that returns#t
when given a value returned byconstructor-name
and#f
for everything else.Each
accessor-name
is a procedure that takes a record of typetype-name
and returns the current value of the corresponding field. It is an error to pass an accessor a value which is not a record of the appropriate type.Each
modifier-name
is a procedure that takes a record of typetype-name
and a value which becomes the new value of the corresponding field. The result (in Kawa) is the empty value#!void
. It is an error to pass a modifier a first argument which is not a record of the appropriate type.Set!ing the value of any of these identifiers has no effect on the behavior of any of their original values.
Here is an example of how you can define a record type named pare
with two fields x
and y
:
(define-record-type pare (kons x y) pare? (x kar set-kar!) (y kdr))
The above defines kons
to be a constructor,
kar
and kdr
to be accessors,
set-kar!
to be a modifier,
and pare?
to be a predicate for pare
s.
(pare? (kons 1 2)) ⇒ #t (pare? (cons 1 2)) ⇒ #f (kar (kons 1 2)) ⇒ 1 (kdr (kons 1 2)) ⇒ 2 (let ((k (kons 1 2))) (set-kar! k 3) (kar k)) ⇒ 3
Kawa compiles the record type into a nested class.
If the define-record-type
appears at module level,
the result is a class that is a member of the module class.
For example if the above pare
class is define in a
module parelib
, then the result is a class
named pare
with the internal JVM name parelib$pare
.
The define-record-type
can appear inside a procedure,
in which case the result is an inner class.
The nested class has a name derived from
the type-name
. If the type-name
is valid Java class name,
that becomes the name of the Java class. If the type-name
has
the form <
(for example name
><pare>
), then name
is used, if possible, for the Java class name. Otherwise, the name
of the Java class is derived by "mangling" the type-name
.
In any case, the package is the same as that of the surrounding module.
Kawa generates efficient code for the resulting functions, without needing to use run-time reflection.