records: Add support for 'innate' fields.

* guix/records.scm (make-syntactic-constructor): Add #:innate parameter.
  [record-inheritance]: Honor it.
  [innate-field?]: New procedure.
  (define-record-type*)[innate-field?]: New procedure.
  Pass #:innate to 'make-syntactic-constructor'.
* tests/records.scm ("define-record-type* & inherit & innate",
  "define-record-type* & thunked & innate"): New tests.
This commit is contained in:
Ludovic Courtès 2015-06-11 22:57:33 +02:00
parent 792798f486
commit 8a16d064fa
2 changed files with 46 additions and 4 deletions

View File

@ -51,6 +51,7 @@ fields, and DELAYED is the list of identifiers of delayed fields."
((_ type name ctor (expected ...) ((_ type name ctor (expected ...)
#:thunked thunked #:thunked thunked
#:delayed delayed #:delayed delayed
#:innate innate
#:defaults defaults) #:defaults defaults)
(define-syntax name (define-syntax name
(lambda (s) (lambda (s)
@ -73,8 +74,11 @@ fields, and DELAYED is the list of identifiers of delayed fields."
#`(make-struct type 0 #`(make-struct type 0
#,@(map (lambda (field index) #,@(map (lambda (field index)
(or (field-inherited-value field) (or (field-inherited-value field)
#`(struct-ref #,orig-record (if (innate-field? field)
#,index))) (wrap-field-value
field (field-default-value field))
#`(struct-ref #,orig-record
#,index))))
'(expected ...) '(expected ...)
(iota (length '(expected ...)))))) (iota (length '(expected ...))))))
@ -84,6 +88,9 @@ fields, and DELAYED is the list of identifiers of delayed fields."
(define (delayed-field? f) (define (delayed-field? f)
(memq (syntax->datum f) 'delayed)) (memq (syntax->datum f) 'delayed))
(define (innate-field? f)
(memq (syntax->datum f) 'innate))
(define (wrap-field-value f value) (define (wrap-field-value f value)
(cond ((thunked-field? f) (cond ((thunked-field? f)
#`(lambda () #,value)) #`(lambda () #,value))
@ -164,7 +171,8 @@ may look like this:
thing? thing?
(name thing-name (default \"chbouib\")) (name thing-name (default \"chbouib\"))
(port thing-port (port thing-port
(default (current-output-port)) (thunked))) (default (current-output-port)) (thunked))
(loc thing-location (innate) (default (current-source-location))))
This example defines a macro 'thing' that can be used to instantiate records This example defines a macro 'thing' that can be used to instantiate records
of this type: of this type:
@ -190,7 +198,8 @@ It is possible to copy an object 'x' created with 'thing' like this:
(thing (inherit x) (name \"bar\")) (thing (inherit x) (name \"bar\"))
This expression returns a new object equal to 'x' except for its 'name' This expression returns a new object equal to 'x' except for its 'name'
field." field and its 'loc' field---the latter is marked as \"innate\", so it is not
inherited."
(define (field-default-value s) (define (field-default-value s)
(syntax-case s (default) (syntax-case s (default)
@ -202,6 +211,7 @@ field."
(define-field-property-predicate delayed-field? delayed) (define-field-property-predicate delayed-field? delayed)
(define-field-property-predicate thunked-field? thunked) (define-field-property-predicate thunked-field? thunked)
(define-field-property-predicate innate-field? innate)
(define (wrapped-field? s) (define (wrapped-field? s)
(or (thunked-field? s) (delayed-field? s))) (or (thunked-field? s) (delayed-field? s)))
@ -251,6 +261,7 @@ field."
(let* ((field-spec #'((field get properties ...) ...)) (let* ((field-spec #'((field get properties ...) ...))
(thunked (filter-map thunked-field? field-spec)) (thunked (filter-map thunked-field? field-spec))
(delayed (filter-map delayed-field? field-spec)) (delayed (filter-map delayed-field? field-spec))
(innate (filter-map innate-field? field-spec))
(defaults (filter-map field-default-value (defaults (filter-map field-default-value
#'((field properties ...) ...)))) #'((field properties ...) ...))))
(with-syntax (((field-spec* ...) (with-syntax (((field-spec* ...)
@ -278,6 +289,7 @@ field."
(field ...) (field ...)
#:thunked #,thunked #:thunked #,thunked
#:delayed #,delayed #:delayed #,delayed
#:innate #,innate
#:defaults #,defaults)))))))) #:defaults #,defaults))))))))
(define* (alist->record alist make keys (define* (alist->record alist make keys

View File

@ -90,6 +90,20 @@
(match b (($ <foo> 1 2) #t)) (match b (($ <foo> 1 2) #t))
(equal? b c))))) (equal? b c)))))
(test-assert "define-record-type* & inherit & innate"
(begin
(define-record-type* <foo> foo make-foo
foo?
(bar foo-bar (innate) (default 42)))
(let* ((a (foo (bar 1)))
(b (foo (inherit a)))
(c (foo (inherit a) (bar 3)))
(d (foo)))
(and (match a (($ <foo> 1) #t))
(match b (($ <foo> 42) #t))
(match c (($ <foo> 3) #t))
(match d (($ <foo> 42) #t))))))
(test-assert "define-record-type* & thunked" (test-assert "define-record-type* & thunked"
(begin (begin
(define-record-type* <foo> foo make-foo (define-record-type* <foo> foo make-foo
@ -139,6 +153,22 @@
(parameterize ((mark (cons 'a 'b))) (parameterize ((mark (cons 'a 'b)))
(eq? (foo-baz y) (mark)))))))) (eq? (foo-baz y) (mark))))))))
(test-assert "define-record-type* & thunked & innate"
(let ((mark (make-parameter #f)))
(define-record-type* <foo> foo make-foo
foo?
(bar foo-bar (thunked) (innate) (default (mark)))
(baz foo-baz (default #f)))
(let* ((x (foo (bar 42)))
(y (foo (inherit x) (baz 'unused))))
(and (procedure? (struct-ref x 0))
(equal? (foo-bar x) 42)
(parameterize ((mark (cons 'a 'b)))
(eq? (foo-bar y) (mark)))
(parameterize ((mark (cons 'a 'b)))
(eq? (foo-bar y) (mark)))))))
(test-assert "define-record-type* & delayed" (test-assert "define-record-type* & delayed"
(begin (begin
(define-record-type* <foo> foo make-foo (define-record-type* <foo> foo make-foo