records: Support custom 'this' identifiers.

This lets record users choose an identifier other than 'this-record'.

* guix/records.scm (make-syntactic-constructor): Add #:this-identifier.
[wrap-field-value]: Honor it.
(define-record-type*): Add form with extra THIS-IDENTIFIER and honor
it.
* tests/records.scm ("define-record-type* & thunked & inherit & custom this"):
New test.
This commit is contained in:
Ludovic Courtès 2019-03-29 22:40:55 +01:00
parent ec12e53736
commit d2be7e3c4b
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
2 changed files with 47 additions and 3 deletions

View File

@ -118,6 +118,7 @@ of TYPE matches the expansion-time ABI."
((_ type name ctor (expected ...) ((_ type name ctor (expected ...)
#:abi-cookie abi-cookie #:abi-cookie abi-cookie
#:thunked thunked #:thunked thunked
#:this-identifier this-identifier
#:delayed delayed #:delayed delayed
#:innate innate #:innate innate
#:defaults defaults) #:defaults defaults)
@ -162,7 +163,7 @@ of TYPE matches the expansion-time ABI."
(define (wrap-field-value f value) (define (wrap-field-value f value)
(cond ((thunked-field? f) (cond ((thunked-field? f)
#`(lambda (x) #`(lambda (x)
(syntax-parameterize ((this-record (syntax-parameterize ((#,this-identifier
(lambda (s) (lambda (s)
(syntax-case s () (syntax-case s ()
(id (id
@ -254,6 +255,7 @@ may look like this:
(define-record-type* <thing> thing make-thing (define-record-type* <thing> thing make-thing
thing? thing?
this-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))
@ -273,7 +275,8 @@ default value specified in the 'define-record-type*' form is used:
The 'port' field is \"thunked\", meaning that calls like '(thing-port x)' will The 'port' field is \"thunked\", meaning that calls like '(thing-port x)' will
actually compute the field's value in the current dynamic extent, which is actually compute the field's value in the current dynamic extent, which is
useful when referring to fluids in a field's value. useful when referring to fluids in a field's value. Furthermore, that thunk
can access the record it belongs to via the 'this-thing' identifier.
A field can also be marked as \"delayed\" instead of \"thunked\", in which A field can also be marked as \"delayed\" instead of \"thunked\", in which
case its value is effectively wrapped in a (delay ) form. case its value is effectively wrapped in a (delay ) form.
@ -352,7 +355,9 @@ inherited."
(syntax-case s () (syntax-case s ()
((_ type syntactic-ctor ctor pred ((_ type syntactic-ctor ctor pred
this-identifier
(field get properties ...) ...) (field get properties ...) ...)
(identifier? #'this-identifier)
(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))
@ -381,15 +386,36 @@ inherited."
field-spec* ...) field-spec* ...)
(define #,(current-abi-identifier #'type) (define #,(current-abi-identifier #'type)
#,cookie) #,cookie)
#,@(if (free-identifier=? #'this-identifier #'this-record)
#'()
#'((define-syntax-parameter this-identifier
(lambda (s)
"Return the record being defined. This macro may
only be used in the context of the definition of a thunked field."
(syntax-case s ()
(id
(identifier? #'id)
(syntax-violation 'this-identifier
"cannot be used outside \
of a record instantiation"
#'id)))))))
thunked-field-accessor ... thunked-field-accessor ...
delayed-field-accessor ... delayed-field-accessor ...
(make-syntactic-constructor type syntactic-ctor ctor (make-syntactic-constructor type syntactic-ctor ctor
(field ...) (field ...)
#:abi-cookie #,cookie #:abi-cookie #,cookie
#:thunked #,thunked #:thunked #,thunked
#:this-identifier #'this-identifier
#:delayed #,delayed #:delayed #,delayed
#:innate #,innate #:innate #,innate
#:defaults #,defaults)))))))) #:defaults #,defaults)))))
((_ type syntactic-ctor ctor pred
(field get properties ...) ...)
;; When no 'this' identifier was specified, use 'this-record'.
#'(define-record-type* type syntactic-ctor ctor pred
this-record
(field get properties ...) ...)))))
(define* (alist->record alist make keys (define* (alist->record alist make keys
#:optional (multiple-value-keys '())) #:optional (multiple-value-keys '()))

View File

@ -210,6 +210,24 @@
(= 40 (foo-bar z)) (= 40 (foo-bar z))
(= -2 (foo-baz z)))))) (= -2 (foo-baz z))))))
(test-assert "define-record-type* & thunked & inherit & custom this"
(let ()
(define-record-type* <foo> foo make-foo
foo? this-foo
(thing foo-thing (thunked)))
(define-record-type* <bar> bar make-bar
bar? this-bar
(baz bar-baz (thunked)))
;; Nest records and test the two self references.
(let* ((x (foo (thing (bar (baz (list this-bar this-foo))))))
(y (foo-thing x)))
(match (bar-baz y)
((first second)
(and (eq? second x)
(bar? first)
(eq? first y)))))))
(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