Declaring and generating fixed sized data type disjoint from all other types, called AGGREGATES.
General other record or object features may be build on top of these aggregates.
(make-datatype designation fieldname ...)
Returns a new datatype. fieldname must be symbols. This is not a functional call in order to avoid potential collisions between similar aggregates but of different kind.
(datatype->designation <my-datatype>)
If <my-datatype> is of datatype kind, then returns its designation.
(datatype->fields <my-datatype>)
If <my-datatype> is of datatype kind, then returns corresponding list of fields.
(create-aggregate <datatype>)
If <datatype> is of datatype kind, then returns corresponding constructor and switch function (see below). <datatype> ensures that the call to create-aggregate is functional, the same aggregate functions are generated if applied to the same <datatype>.
create-aggregate returns 2 functions as values:
make-<aggregate> is a function taking a fixed number of arguments as specified by the <datatype> and returning a new aggregate containing these arguments as aggregate components.
<aggregate>-switch is explained below.
The data created by make-<aggregate> and selected by <aggregate>-switch are required to conform to <my-datatype> properties (currently only arity).
Either
However data are aggregated in order to retrieve many part of it and not only one. Solution 2 requires to perform redundant check for each accessed field and moreover the error in general is fatal to the program execution. Solution 1 alone is unsatisfactory as if an unsafe access procedure is applied to not of the correct kind data then random and unwanted behaviors may appear.
Another solution is to group together data type checking with accessing in a case analysis function (per aggregate types) :
(<aggregate>-switch <aggregate-case> <else-case>) = (lambda (<obj>) ...)
Two cases are possible: if the data <obj> is of <aggregate> corresponding datatype kind then <aggregate-case> function is called with the components of the <obj> data, else <else-case> is called with <obj>.
The idea behind my-datatype-switch function is to open an environment with bindings for corresponding aggregate components.
A data is a variant if it is one kind of a list of aggregates.
For instance, one could view the following classic types as variant:
Convenience macro for variants may be provided as in [see 2]:
(define-syntax variant-case (syntax-rules (else) ((variant-case <obj>) (error "variant-case: all case exhausted " <obj>)) ((variant-case <obj> (else <body> ...)) (begin <body> ...)) ((variant-case <obj> (<aggregate-switch> (<var> ...) <body> ...) rest ...) ((<aggregate-switch> (lambda (<var> ...) <body> ...) (lambda (<obj>) (variant-case <obj> rest ...))) <obj>))))Disjointness issue raised and proposal:
[1] Jonathan A. Rees. "User-defined data types". Lisp Pointers. 'The Scheme of Things' (column). 1993
For variant-case to destructure records:
[2] Daniel P. Friedman, Mitchell Wand, and Christopher T. Haynes. Essentials of Programming Languages. MIT Press and McGraw-Hill, 1991.
RTD (datatype) functions:
[3] Jonathan A. Rees, Norman I. Adams IV and James R. Meehan. "The T manual". Yale University Computer Science Department. 1984.