Skip to content

Commit bf08ef2

Browse files
authored
struct: add #:properties prop-alist-expr option
Add a #:properties option to struct that takes an association list (same type as make-struct-type). This makes struct a better target for macros, since the calculation of properties can be separated from the rest of the struct declaration. Multiple property values can be calculated in a single expression position, making it easier for property values to share intermediate calculations and syntax bindings.
1 parent 3104f29 commit bf08ef2

File tree

3 files changed

+69
-10
lines changed

3 files changed

+69
-10
lines changed

pkgs/racket-doc/scribblings/reference/define-struct.scrbl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
(code:line #:auto-value auto-expr)
2424
(code:line #:guard guard-expr)
2525
(code:line #:property prop-expr val-expr)
26+
(code:line #:properties prop-list-expr)
2627
(code:line #:transparent)
2728
(code:line #:prefab)
2829
(code:line #:sealed)
@@ -133,9 +134,10 @@ procedure, respectively. See @racket[make-struct-type] for more
133134
information on these attributes of a structure type. The
134135
@racket[#:property] option, which can be supplied
135136
multiple times, attaches a property value to the structure type; see
136-
@secref["structprops"] for more information on properties. The
137-
@racket[#:transparent] option is a shorthand for @racket[#:inspector
138-
#f].
137+
@secref["structprops"] for more information on properties.
138+
The @racket[#:properties] option, which can be supplied multiple times,
139+
accepts multiple properties and their values as an association list.
140+
The @racket[#:transparent] option is a shorthand for @racket[#:inspector #f].
139141

140142
@examples[#:eval posn-eval
141143
(struct point (x y) #:inspector #f)
@@ -307,7 +309,8 @@ cp
307309
For serialization, see @racket[define-serializable-struct].
308310

309311
@history[#:changed "6.9.0.4" @elem{Added @racket[#:authentic].}
310-
#:changed "8.0.0.7" @elem{Added @racket[#:sealed].}]}
312+
#:changed "8.0.0.7" @elem{Added @racket[#:sealed].}
313+
#:changed "8.17.0.4" @elem{Added @racket[#:properties].}]}
311314

312315

313316
@defform[(struct-field-index field-id)]{

pkgs/racket-test-core/tests/racket/struct.rktl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,31 @@
12401240

12411241
;; ----------------------------------------
12421242

1243+
(let ()
1244+
(define-values (prop:a a? a-ref) (make-struct-type-property 'a))
1245+
(define-values (prop:b b? b-ref) (make-struct-type-property 'b))
1246+
(define-values (prop:c c? c-ref) (make-struct-type-property 'c))
1247+
(struct s (x y)
1248+
#:properties (list (cons prop:a "abc") (cons prop:b "xyz"))
1249+
#:property prop:procedure (lambda (self arg) arg)
1250+
#:properties (list (cons prop:c 'here)))
1251+
1252+
(test "abc" a-ref (s 1 2))
1253+
(test "xyz" b-ref (s 1 2))
1254+
(test 'here c-ref (s 1 2))
1255+
(test 123 (s 1 2) 123)
1256+
1257+
;; Allow #:properties with #:prefab, dynamic error if non-empty
1258+
(struct ps1 (x y) #:prefab #:properties null)
1259+
(struct ps2 (x [y #:mutable]) #:properties null #:prefab)
1260+
(err/rt-test (let ()
1261+
(struct pbad (x y)
1262+
#:prefab
1263+
#:properties (list (cons prop:procedure void)))
1264+
(void))))
1265+
1266+
;; ----------------------------------------
1267+
12431268
(require (for-syntax racket/struct-info))
12441269

12451270
(let ()

racket/collects/racket/private/define-struct.rkt

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,17 @@
143143
(raise-argument-error name "symbol?" what))
144144
what)
145145

146+
(define (check-property-alist name what)
147+
(unless (and (list? what)
148+
(andmap (lambda (elem)
149+
(and (pair? elem)
150+
(struct-type-property? (car elem))))
151+
what))
152+
(raise-argument-error name
153+
"(listof (cons struct-type-property? any/c))"
154+
what))
155+
what)
156+
146157
(define-syntax (define-struct* stx)
147158
(syntax-case stx ()
148159
[(_ . rest)
@@ -260,6 +271,7 @@
260271
(#:inspector . #f)
261272
(#:auto-value . #f)
262273
(#:props . ())
274+
(#:proplists . ())
263275
(#:mutable . #f)
264276
(#:guard . #f)
265277
(#:constructor-name . #f)
@@ -312,6 +324,17 @@
312324
(cons (cons (cadr p) (caddr p))
313325
(lookup config '#:props)))
314326
nongen?)]
327+
[(eq? '#:properties (syntax-e (car p)))
328+
(check-exprs 1 p #f)
329+
(when nongen?
330+
;; no error, since `#:properties null` should be allowed for prefab
331+
(void))
332+
(loop (cddr p)
333+
(extend-config config
334+
'#:proplists
335+
(cons #`(check-property-alist '#,fm #,(cadr p))
336+
(lookup config '#:proplists)))
337+
nongen?)]
315338
[(eq? '#:methods (syntax-e (car p)))
316339
;; #:methods gen:foo [(define (meth1 x ...) e ...) ...]
317340
(check-exprs 2 p "argument")
@@ -494,7 +517,7 @@
494517
(car field-stxes))]
495518
[else
496519
(loop (cdr fields) (cdr field-stxes) #f)]))])
497-
(let*-values ([(inspector super-expr props auto-val guard ctor-name ctor-only?
520+
(let*-values ([(inspector super-expr props proplists auto-val guard ctor-name ctor-only?
498521
reflect-name-expr mutable?
499522
omit-define-values? omit-define-syntaxes?
500523
info-name name-only?)
@@ -512,6 +535,7 @@
512535
(cons (cons #'prop:sealed #'#t)
513536
l)
514537
l)))
538+
(lookup config '#:proplists)
515539
(lookup config '#:auto-value)
516540
(lookup config '#:guard)
517541
(lookup config '#:constructor-name)
@@ -672,11 +696,18 @@
672696
#,(- (length fields) auto-count)
673697
#,auto-count
674698
#,auto-val
675-
#,(if (null? props)
676-
#'null
677-
#`(list #,@(map (lambda (p)
678-
#`(cons #,(car p) #,(cdr p)))
679-
props)))
699+
#,(cond
700+
[(and (null? props) (null? proplists))
701+
#'null]
702+
[(null? proplists)
703+
#`(list #,@(map (lambda (p)
704+
#`(cons #,(car p) #,(cdr p)))
705+
props))]
706+
[else
707+
#`(list* #,@(map (lambda (p)
708+
#`(cons #,(car p) #,(cdr p)))
709+
props)
710+
(append #,@proplists))])
680711
#,(or inspector
681712
#`(current-inspector))
682713
#f

0 commit comments

Comments
 (0)