Accessor wrappers allow you to redirect a struct accessor to do whatever you want. Good examples would be to force a promise or access a database.
#lang racket
(require struct-plus-plus "db/dbh.rkt") ; dbh.rkt provides (dbh) which returns a pre-initialized database connection
(struct++ dog ([name (or/c string? promise?) #:wrap-accessor (wrap-accessor force)]))
(define fido (dog++ #:name (lazy "fido")))
(displayln
(~a " (dog-name fido), the default struct accessor, returns: " (dog-name fido)
"\n before dotted accessor, promise was forced?: " (promise-forced? (dog-name fido))
"\n (dog.name fido), the struct++ dotted accessor, returns: " (dog.name fido)
"\n after dotted accessor, promise was forced?: " (promise-forced? (dog-name fido))))
; a trivial ORM
(struct++ person ([db-id integer?]
[(conn (dbh)) connection?] ; default argument creates the connection
[(username #f) string? #:wrap-accessor (lambda (the-person val) (get-username the-person))]
[(go-boom #f) integer? #:wrap-accessor (lambda (the-person val) (get-username the-person))]
)
#:transparent)
(define (get-username the-person)
(query-value (person.conn the-person)
"select username from users where id = ?"
(person.db-id the-person)))
(define smith (person++ #:db-id 2))
(displayln (~a "(person.username smith) returns: " (person.username smith)))
(display (~a "(person.go-boom smith) dies because the returned value does not match the field contract: "))
(person.go-boom smith)
The output is:
(dog-name fido), the default struct accessor, returns: #<promise!fido>
before dotted accessor, promise was forced?: #f
(dog.name fido), the struct++ dotted accessor, returns: fido
after dotted accessor, promise was forced?: #t
(person.username smith) returns: smith@ursaminor.edu
(person.go-boom smith) dies because the returned value does not match the field contract:
; person.go-boom: broke its own contract
; promised: integer?
; produced: "smith@ursaminor.edu"
; in: the range of
; (-> person? integer?)
; contract from: (function person.go-boom)
; blaming: (function person.go-boom)
; (assuming the contract is correct)
; at: /Users/dstorrs/test.rkt
; Context (plain; to see better errortrace context, re-run with C-u prefix):
; /Applications/Racket_v8.4/collects/racket/contract/private/blame.rkt:346:0 raise-blame-error
; /Applications/Racket_v8.4/collects/racket/contract/private/arrow-higher-order.rkt:375:33
They come with one caveat which is that they can return a result different from what's actually there, in which case the wrapped accessor would give a different result than you would get from, e.g., match
.
The fact that the default accessors created by struct
do not use the wrapper is a feature, not a bug.