mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
Add support for immutable fields in core records
* module/ice-9/boot-9.scm (make-record-type): Allow (mutable NAME) or (immutable NAME) as a field name, and record field mutability in a bitfield. (record-modifier): Throw an error if the field isn't mutable. * test-suite/tests/records.test ("records"): Add tests. * doc/ref/api-data.texi (Records): Update.
This commit is contained in:
parent
f963bdf02d
commit
315fabdfe7
3 changed files with 78 additions and 8 deletions
|
@ -8637,8 +8637,10 @@ Create and return a new @dfn{record-type descriptor}.
|
||||||
|
|
||||||
@var{type-name} is a string naming the type. Currently it's only used
|
@var{type-name} is a string naming the type. Currently it's only used
|
||||||
in the printed representation of records, and in diagnostics.
|
in the printed representation of records, and in diagnostics.
|
||||||
@var{field-names} is a list of symbols naming the fields of a record
|
@var{field-names} is a list of elements of the form @code{(immutable
|
||||||
of the type. Duplicates are not allowed among these symbols.
|
@var{name})}, @code{(mutable @var{name})}, or @var{name}, where
|
||||||
|
@var{name} are symbols naming the fields of a record of the type.
|
||||||
|
Duplicates are not allowed among these symbols.
|
||||||
|
|
||||||
@example
|
@example
|
||||||
(make-record-type "employee" '(name age salary))
|
(make-record-type "employee" '(name age salary))
|
||||||
|
@ -8680,6 +8682,11 @@ the record type. @xref{rnrs records procedural}, for full details. The
|
||||||
@code{#:opaque?} flag is used by Guile's R6RS layer to record this
|
@code{#:opaque?} flag is used by Guile's R6RS layer to record this
|
||||||
information. The default is determined by whether the parent type, if
|
information. The default is determined by whether the parent type, if
|
||||||
any, was opaque.
|
any, was opaque.
|
||||||
|
|
||||||
|
Fields are mutable by default, meaning that @code{record-modifier} will
|
||||||
|
return a procedure that can update a record in place. Specifying a
|
||||||
|
field using the form @code{(immutable @var{name})} instead marks a field
|
||||||
|
as immutable.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Procedure} record-constructor rtd
|
@deffn {Scheme Procedure} record-constructor rtd
|
||||||
|
|
|
@ -1191,10 +1191,10 @@ VALUE."
|
||||||
;;
|
;;
|
||||||
;; It should print OBJECT to PORT.
|
;; It should print OBJECT to PORT.
|
||||||
|
|
||||||
;; 0: type-name, 1: fields, 2: constructor, 3: flags, 4: parents
|
;; 0: type-name, 1: fields, 2: constructor, 3: flags, 4: parents 5: mutable bitmask
|
||||||
(define record-type-vtable
|
(define record-type-vtable
|
||||||
(let ((s (make-vtable (string-append standard-vtable-fields
|
(let ((s (make-vtable (string-append standard-vtable-fields
|
||||||
"pwpwpwpwpw")
|
"pwpwpwpwpwpw")
|
||||||
(lambda (s p)
|
(lambda (s p)
|
||||||
(display "#<record-type " p)
|
(display "#<record-type " p)
|
||||||
(display (record-type-name s) p)
|
(display (record-type-name s) p)
|
||||||
|
@ -1235,6 +1235,11 @@ VALUE."
|
||||||
(error 'not-a-record-type rtd))
|
(error 'not-a-record-type rtd))
|
||||||
(struct-ref rtd (+ 4 vtable-offset-user)))
|
(struct-ref rtd (+ 4 vtable-offset-user)))
|
||||||
|
|
||||||
|
(define (record-type-mutable-fields rtd)
|
||||||
|
(unless (record-type? rtd)
|
||||||
|
(error 'not-a-record-type rtd))
|
||||||
|
(struct-ref rtd (+ 5 vtable-offset-user)))
|
||||||
|
|
||||||
(define prefab-record-types
|
(define prefab-record-types
|
||||||
(make-hash-table))
|
(make-hash-table))
|
||||||
|
|
||||||
|
@ -1329,12 +1334,36 @@ VALUE."
|
||||||
(cons field tail))))
|
(cons field tail))))
|
||||||
|
|
||||||
(define computed-fields
|
(define computed-fields
|
||||||
(begin
|
(let ((fields (map (lambda (field)
|
||||||
|
(cond
|
||||||
|
((symbol? field) field)
|
||||||
|
(else
|
||||||
|
(unless (and (pair? field)
|
||||||
|
(memq (car field) '(mutable immutable))
|
||||||
|
(pair? (cdr field))
|
||||||
|
(null? (cddr field)))
|
||||||
|
(error (error "bad field declaration" field)))
|
||||||
|
(cadr field))))
|
||||||
|
fields)))
|
||||||
(check-fields fields)
|
(check-fields fields)
|
||||||
(if parent
|
(if parent
|
||||||
(append-fields (record-type-fields parent) fields)
|
(append-fields (record-type-fields parent) fields)
|
||||||
fields)))
|
fields)))
|
||||||
|
|
||||||
|
(define mutable-fields
|
||||||
|
(let lp ((fields fields)
|
||||||
|
(i (if parent (length (record-type-fields parent)) 0))
|
||||||
|
(mutable (if parent (record-type-mutable-fields parent) 0)))
|
||||||
|
(if (null? fields)
|
||||||
|
mutable
|
||||||
|
(let ((field (car fields)))
|
||||||
|
(lp (cdr fields)
|
||||||
|
(1+ i)
|
||||||
|
(if (or (not (pair? field))
|
||||||
|
(eq? (car field) 'mutable))
|
||||||
|
(logior mutable (ash 1 i))
|
||||||
|
mutable))))))
|
||||||
|
|
||||||
(define name-sym
|
(define name-sym
|
||||||
(cond
|
(cond
|
||||||
((symbol? type-name) type-name)
|
((symbol? type-name) type-name)
|
||||||
|
@ -1359,7 +1388,8 @@ VALUE."
|
||||||
(equal? (record-type-fields rtd) computed-fields)
|
(equal? (record-type-fields rtd) computed-fields)
|
||||||
(not printer)
|
(not printer)
|
||||||
(equal? (record-type-properties rtd) properties)
|
(equal? (record-type-properties rtd) properties)
|
||||||
(equal? (record-type-parents rtd) parents))
|
(equal? (record-type-parents rtd) parents)
|
||||||
|
(equal? (record-type-mutable-fields rtd) mutable-fields))
|
||||||
(error "prefab record type declaration incompatible with previous"
|
(error "prefab record type declaration incompatible with previous"
|
||||||
rtd))
|
rtd))
|
||||||
rtd))
|
rtd))
|
||||||
|
@ -1374,7 +1404,8 @@ VALUE."
|
||||||
computed-fields
|
computed-fields
|
||||||
#f ; Constructor initialized below.
|
#f ; Constructor initialized below.
|
||||||
properties
|
properties
|
||||||
parents)))
|
parents
|
||||||
|
mutable-fields)))
|
||||||
|
|
||||||
(struct-set! rtd (+ vtable-offset-user 2)
|
(struct-set! rtd (+ vtable-offset-user 2)
|
||||||
(make-constructor rtd (length computed-fields)))
|
(make-constructor rtd (length computed-fields)))
|
||||||
|
@ -1446,6 +1477,8 @@ VALUE."
|
||||||
(pos (or (list-index (record-type-fields rtd) field-name)
|
(pos (or (list-index (record-type-fields rtd) field-name)
|
||||||
(error 'no-such-field field-name)))
|
(error 'no-such-field field-name)))
|
||||||
(pred (record-predicate rtd)))
|
(pred (record-predicate rtd)))
|
||||||
|
(unless (logbit? pos (record-type-mutable-fields rtd))
|
||||||
|
(error "field is immutable" rtd field-name))
|
||||||
(lambda (obj val)
|
(lambda (obj val)
|
||||||
(unless (pred obj)
|
(unless (pred obj)
|
||||||
(scm-error 'wrong-type-arg "record-modifier"
|
(scm-error 'wrong-type-arg "record-modifier"
|
||||||
|
|
|
@ -166,4 +166,34 @@
|
||||||
(pass-if (not (record-type-opaque? b)))
|
(pass-if (not (record-type-opaque? b)))
|
||||||
(pass-if (record-type-opaque? c))
|
(pass-if (record-type-opaque? c))
|
||||||
(pass-if-exception "non-opaque" '(misc-error . "opaque")
|
(pass-if-exception "non-opaque" '(misc-error . "opaque")
|
||||||
(make-record-type 'd '() #:opaque? #f #:parent a)))))
|
(make-record-type 'd '() #:opaque? #f #:parent a))))
|
||||||
|
|
||||||
|
(with-test-prefix "immutable fields"
|
||||||
|
(let ()
|
||||||
|
(define a (make-record-type 'a '(s t (mutable u) (immutable v))
|
||||||
|
#:extensible? #t))
|
||||||
|
(define b (make-record-type 'b '(w (immutable x)) #:parent a))
|
||||||
|
|
||||||
|
(pass-if-exception "bad field" '(misc-error . "field")
|
||||||
|
(make-record-type 'a '("foo")))
|
||||||
|
(pass-if-exception "bad field" '(misc-error . "field")
|
||||||
|
(make-record-type 'a '((mutable u x))))
|
||||||
|
(pass-if-exception "bad field" '(misc-error . "field")
|
||||||
|
(make-record-type 'a '((qux u))))
|
||||||
|
(pass-if-equal (record-type-mutable-fields a) #b0111)
|
||||||
|
(pass-if-equal (record-type-mutable-fields b) #b010111)
|
||||||
|
|
||||||
|
(pass-if (procedure? (record-modifier a 's)))
|
||||||
|
(pass-if (procedure? (record-modifier a 't)))
|
||||||
|
(pass-if (procedure? (record-modifier a 'u)))
|
||||||
|
(pass-if-exception "immutable" '(misc-error . "immutable")
|
||||||
|
(record-modifier a 'v))
|
||||||
|
|
||||||
|
(pass-if (procedure? (record-modifier b 's)))
|
||||||
|
(pass-if (procedure? (record-modifier b 't)))
|
||||||
|
(pass-if (procedure? (record-modifier b 'u)))
|
||||||
|
(pass-if-exception "immutable" '(misc-error . "immutable")
|
||||||
|
(record-modifier b 'v))
|
||||||
|
(pass-if (procedure? (record-modifier b 'w)))
|
||||||
|
(pass-if-exception "immutable" '(misc-error . "immutable")
|
||||||
|
(record-modifier b 'x)))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue