For the record, a self-plug:

#lang racket

(require struct-plus-plus)

(struct foo-guard (bar baz)
  #:guard (struct-guard/c any/c list?))


(struct/contract foo-contract ([bar any/c]
                               [baz list?]))

(struct++ foo-spp  ([bar any/c]
                    [baz list?]))

(display "#:guard:         ")

(time
 (for ([i 1000000])
   (foo-guard 'yeah '(buddy))))

(display "struct/contract: ")
(time
 (for ([i 1000000])
   (foo-contract 'yeah '(buddy))))

(display "struct++:        ")
(time
 (for ([i 1000000])
   (foo-spp++ #:bar 'yeah #:baz '(buddy))))

#:guard:         cpu time: 3335 real time: 3361 gc time: 90
struct/contract: cpu time: 217 real time: 218 gc time: 3
struct++:        cpu time: 1255 real time: 1262 gc time: 23

The struct++ version is slower at creation but gives you more security
guarantees later on in the form of functional setters, business rules, and
wrapper functions to normalize data.  For example:


; This accepts a symbol or string and forces it to string.

(struct++ baz-spp ([foo (or/c symbol? string?) ~a]) #:transparent)
(display "Result: ") (println (baz-spp++ #:foo 'bob))
Result: (baz-spp "bob")

; Any struct++ struct will throw an exception if given invalid data

(struct++ bar-spp ([foo integer?]))
(display "Throws: ") (bar-spp++ #:foo 'bob)
Throws:
; bar-spp++: contract violation

;   expected: integer?

;   given: 'bob

;   in: the #:foo argument of

;       (-> #:foo integer? bar-spp?)

;   contract from: (function bar-spp++)

;   blaming: /Users/dstorrs/bmtc_dev/app/test.rkt

;    (assuming the contract is correct)

;   at: /Users/dstorrs/bmtc_dev/app/test.rkt

; Context:

;  "/Users/dstorrs/bmtc_dev/app/test.rkt":1:1 [running body]



; Example of more heavily verified struct.  It will accept a string or

; symbol for name and force it to string.  It will round the age down

; so it shows only years. It will default the 'gender' field to the

; symbol 'unknown.  It will make a database connection and verify that

; the department ID is valid.  (In practice you would want to check

; this against an in-RAM table instead of going to disk.)
;
(define (dbh) "mock function that should return a database handle")

(struct++ person ([name (or/c symbol? string?) ~a]
                  [age  positive? (compose inexact->exact floor)]
                  [(gender 'unknown) (or/c 'male 'female 'other 'unknown)]
                  [user-id exact-positive-integer?])
          (#:rule ("department-id exists in the database"
                   #:check (user-id)
                   [(not (null? (query-rows (dbh) ; get database handle

                                            "SELECT id FROM users WHERE
id=$1"
                                            user-id)))]))
          #:transparent)
(display "Result: ")(println (person++ #:name 'bob #:age 18.5 #:user-id 2))

Result: (person "bob" 18 'unknown 2)

It also provides reflection, functional setters (dotted and dashed
versions), transformation rules, and other things.
https://docs.racket-lang.org/struct-plus-plus/index.html





On Wed, Sep 2, 2020 at 10:43 PM Christopher Lemmer Webber <
cweb...@dustycloud.org> wrote:

> Philip McGrath writes:
>
> > On Wed, Sep 2, 2020 at 3:41 PM Christopher Lemmer Webber <
> > cweb...@dustycloud.org> wrote:
> >
> >> Unfortunately I can't use #:methods with struct/contract so I'm stuck
> >> with the slow one if I want a contract on the struct?
> >>
> >
> > For another option (though you may already know this), I'd advocate for
> > using the `struct` sub-form of `contract-out` and drawing the module
> > boundary as tightly as needed to make it a sensible boundary for trust,
> > potentially by using submodules.
>
> Yes... probably what I should do in the future.
>
> > Since you mention `#:methods` in particular, you should be aware of some
> > subtle corners that make it tricky (and potentially expensive at runtime)
> > to protect  `racket/generic` methods comprehensively with contracts.
> (Here's
> > a pointer to some discussions.
> > <https://github.com/racket/racket/issues/1710>) I think just working
> with
> > struct-type properties can make sense when you don't really need most of
> > the features `racket/generic` gives you.
>
> :O
>
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-users+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/racket-users/87wo1ba8c1.fsf%40dustycloud.org
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-users/CAE8gKodHDOVU3y7V1ZZbEn_hSEP%2BYbbtAqTShRdgMeNX2bWLyA%40mail.gmail.com.

Reply via email to