This SRFI is currently in withdrawn status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to
firstname.lastname@example.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
This SRFI introduces a macro, DEFINE-RECORD-LAMBDA, that defines a set of procedures, that is, a group of constructors and a predicate. The constructors also make a group of procedures, namely record lambdas, that have no explicit field accessors and mutators. They can have various kinds of fields, such as common fields, required fields, optional fields, automatic fields, read-only fields, read-write fields, invisible fields, immutable fields, and virtual fields.
This SRFI is based on SRFI 100. Unlike the define-lambda-object of SRFI 100:
A record lambda created by a constructor procedure is a procedure whose first argument is a field index or symbolized field name that is used to identify fields. It depends on the constructor. The record lambda plays the role of the accessor and mutator of each field and makes the explicit or implicit accessors and mutators unnecessary. In addition, this reduces the role of the predicate procedure.
This macro works not only as DEFINE-RECORD-TYPE with required fields but also as DEFSTRUCT of Common Lisp with optional fields. Each field except required fields can be used as a procedure that changes the values of the other fields. As a result, the record lambda itself has data and methods as well as their accessors and mutators.
(define-record-lambda <group> <field spec>) <field spec> --> <common field>* <required field>* <optional field>* <automatic field>* <common field> --> | (,@<field> <default>) ;read-only automatic common field | ((,@<field>) <default>) ;read-write automatic common field | (',@<field> <default>) ;immutable automatic common field | (`,@<field> <default>) ;invisible automatic common field <required field> --> | <field> ;read-only required field | (<field>) ;read-write required field | 'field ;immutable required field | `field ;invisible required field <optional field> --> | (<field> <default>) ;read-only optional field | ((<field>) <default>) ;read-write optional field | ('<field> <default>) ;immutable optional field | (`<field> <default>) ;invisible optional field <automatic field> --> | (,<field> <default>) ;read-only automatic field | ((,<field>) <default>) ;read-write automatic field | (',<field> <default>) ;immutable automatic field | (`,<field> <default>) ;invisible automatic field | (,,<field> <default>) ;virtual automatic field
The name of the first <constructor> is generated by prefixing "make-" to the group name, or by prefixing "make-" and postfixing "-by-name" to the group name. And the name of the second <constructor> is generated by appending "/s" to the name of the first <constructor>. While the record lambda that is constructed by the former uses field indices to identify the fields, the one constructed by the latter uses symbols. And the average time required to access a randomly chosen field is more for the record lambda that is constructed by the latter than for the one by the former.
The name of <predicate> is generated by adding a question mark ("?") to the end of the group name.
The <group> and <field> must be identifiers.
Each <default> is an <expression> that is evaluated in an environment in which the values of all the previous <field>s are visible.
The define-record-lambda form is a definition and can appear anywhere any other <definition> can appear. Each time define-record-lambda form is evaluated, a new group is created with distinct <group>, <constructor>, and <predicate> procedures.
The <group> is bound to a procedure of one argument. It has information on its <constructor>s, <predicate>, and <field>s.
The <constructor> is bound to a procedure that takes at least as many arguments as the number of <required field>s. Whenever it is called, it returns a record lambda of the <group>, namely a procedure. Its first argument must be a symbol of the same name as <field> or a field index whose name is composed of the group name and field name. The record lambda becomes an accessor procedure of each <field> in case of one argument and a mutator procedure of each <field> in case of two arguments where the second argument is a new field value.
The names of <field>s are used to access the <field>s. So they must be distinct. The read-write fields can be modified, whereas any attempt to modify the values of the read-only fields via mutators signals an error.
Note: The read-only fields are not immutable. Their values, for instance, can be modified by other fields whose values work like their mutators.
The <required field> is initialized to the first one of the remaining arguments. If there are no more remaining arguments, an error is signaled.The initialization of the <optional field>s is done by two types of <constructor>s:
The <common field>s are also <automatic field>s, and initialized to each corresponding <default> that is evaluated at the time the define-record-lambda form is evaluated, and the values are shared with all the record lambdas that are made by the constructors of the define-record-lambda form.
The other <automatic field>s except <virtual field>s are initialized to each corresponding <default> that is evaluated at the time the record lambda is made by a constructor. Each <default> of <virtual field>s is evaluated each time when the field is accessed.
The <invisible field>s are externally nonexistent fields, that is, the fields are invisible outside of the define-record-lambda form but accessible inside of it. On the contrary, the <immutable field>s and <virtual field>s are internally nonexistent fields, that is, the fields are visible outside of the define-record-lambda form but inaccessible inside of it.
The <predicate> is a predicate procedure that returns #t for record lambdas constructed by <constructor> or <constructor>s of a <group> and #f for everything else.
Examples ;; invisible & immutable field example (define-record-lambda stack (`stack '()) ;invisible optional (',push (lambda (s) (set! stack (cons s stack)))) ;immutable automatic (,,pop (if (null? stack) ;virtual automatic (error "null stack" stack) (let ((s (car stack))) (set! stack (cdr stack)) s)))) (define st (make-stack)) (st stack-stack) -> error stack-stack: undefined (st stack-pop) -> error null stack () (st stack-push 2) -> error invisible or immutable field ((st stack-push) "rose") ((st stack-push) "lily") ((st stack-push) "sunflower") (st stack-pop) -> "sunflower" (st stack-pop) -> "lily" (st stack-pop) -> "rose" (st stack-pop) -> error null stack () (stack 'constructor) -> ((make-stack . make-stack-by-name) (make-stack/s . make-stack-by-name/s)) (stack 'predicate) -> stack? (stack 'field) -> ((total (read-write-field ()) (read-only-field (push pop)) (invisible-field (stack))) (types (common-field ()) (required-field ()) (optional-field (stack)) (automatic-field (push pop)) (immutable-field (push)) (invisible-field (stack)) (virtual-field (pop)))) (stack 'index) -> ((stack-push . 0) (stack-pop . 1)) ;; virtual field example (let () (define-record-lambda circle ((radius) 1) ;read-write optional (',area! (lambda (a) (set! radius (sqrt (/ a 3.14))))) ;immutable automatic (,,area (* 3.14 radius radius))) ;virtual automatic (list (let ((c (make-circle))) ;index (list (c circle-area) (c circle-radius) (begin ((c circle-area!) (* 4 (c circle-area))) (c circle-area)) (c circle-radius) (begin (c circle-radius 10) (c circle-area)) (c circle-radius))) (let ((c (make-circle/s))) ;symbol (list (c 'area) (c 'radius) (begin ((c 'area!) (* 4 (c 'area))) (c 'area)) (c 'radius) (begin (c 'radius 10) (c 'area)) (c 'radius))))) -> ((3.14 1 12.56 2.0 314.0 10) (3.14 1 12.56 2.0 314.0 10)) ;; common field example (let () (define-record-lambda record (,@total-record 0) ;read-only automatic common (,@total-connect 0) ;read-only automatic common (,order-of-birth (begin ;read-only automatic (set! total-record (+ 1 total-record)) total-record)) (,individual-connect 0) ;read-only automatic (,,connect (begin ;virtual automatic (set! individual-connect (+ 1 individual-connect)) (set! total-connect (+ 1 total-connect)) 'connect))) (let* ((record-1 (make-record/s)) (record-2 (make-record/s)) (record-3 (make-record/s)) (record-4 (make-record/s)) (record-5 (make-record/s))) (record-1 'connect) (record-1 'connect) (record-1 'connect) (record-1 'connect) (record-1 'connect) (record-2 'connect) (record-2 'connect) (record-2 'connect) (record-2 'connect) (record-3 'connect) (record-3 'connect) (record-3 'connect) (record-4 'connect) (record-4 'connect) (record-5 'connect) (map (lambda (o) (list (list 'order-of-birth (o 'order-of-birth)) (list 'individual-connect (o 'individual-connect)) (list 'total-record (o 'total-record)) (list 'total-connect (o 'total-connect)))) (list record-1 record-2 record-3 record-4 record-5)))) -> (((order-of-birth 1) (individual-connect 5) (total-record 5) (total-connect 15)) ((order-of-birth 2) (individual-connect 4) (total-record 5) (total-connect 15)) ((order-of-birth 3) (individual-connect 3) (total-record 5) (total-connect 15)) ((order-of-birth 4) (individual-connect 2) (total-record 5) (total-connect 15)) ((order-of-birth 5) (individual-connect 1) (total-record 5) (total-connect 15)))
Sample implementations are provided based on both
define-syntax (syntax-rules with syntax-case or explicit
renaming macro) and
define-macro. They're available both
in the Github
repo and in this
|[R6RS]||Michael Sperber, R. Kent Dybvig, Matthew Flatt, and Anton von Straaten: Revised(6) Report on the Algorithmic Language Scheme.http://www.r6rs.org|
|[SRFI 9]||Richard Kelsey: Defining Record Type.http://srfi.schemers.org/srfi-9|
|[SRFI 100]||Joo ChurlSoo: Define-lambda-object.http://srfi.schemers.org/srfi-100/|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.