added contract-struct (like define-contract-struct, but with a

maker whose name does not begin with 'make-')
This commit is contained in:
Robby Findler 2010-08-10 22:20:33 -05:00
parent ae1304789d
commit 367779fd27
5 changed files with 439 additions and 390 deletions

View File

@ -40,7 +40,8 @@
(provide
opt/c define-opt/c ;(all-from "private/contract-opt.rkt")
(except-out (all-from-out racket/contract/private/ds)
lazy-depth-to-look)
lazy-depth-to-look
contract-struct)
(all-from-out racket/contract/private/base)
(all-from-out racket/contract/private/provide)

View File

@ -3,9 +3,10 @@
build-func-params
build-clauses
build-enforcer-clauses
generate-arglists)
generate-arglists
(struct-out contract-struct-transformer))
(require "opt-guts.rkt")
(require racket/struct-info "opt-guts.rkt")
(require (for-template racket/base))
#|
@ -382,3 +383,7 @@ which are then called when the contract's fields are explored
(for-each
(λ (id) (unless (identifier? id) (raise-syntax-error 'struct/dc "expected identifier" stx id)))
(syntax->list (syntax (id ...))))]))
(struct contract-struct-transformer (info proc)
#:property prop:struct-info (λ (ctc) (contract-struct-transformer-info ctc))
#:property prop:procedure 1)

View File

@ -25,6 +25,7 @@ it around flattened out.
(for-syntax "opt-guts.rkt"))
(provide define-contract-struct
contract-struct
make-opt-contract/info
;set-opt-contract/info-enforcer!
@ -36,300 +37,312 @@ it around flattened out.
unknown?
synthesized-value)
(define-syntax (define-contract-struct stx)
(syntax-case stx ()
[(_ name (fields ...))
(syntax (define-contract-struct name (fields ...) (current-inspector)))]
[(_ name (fields ...) inspector)
(and (identifier? (syntax name))
(andmap identifier? (syntax->list (syntax (fields ...)))))
(let* ([mutable? (syntax-e (syntax mutable?))]
[add-suffix
(λ (suffix)
(datum->syntax (syntax name)
(string->symbol
(string-append (symbol->string (syntax-e (syntax name)))
suffix))
stx))]
[struct-names (build-struct-names (syntax name)
(syntax->list (syntax (fields ...)))
#f
#t
stx)]
[struct:-name/val (list-ref struct-names 0)]
[struct-maker/val (list-ref struct-names 1)]
[predicate/val (list-ref struct-names 2)]
[selectors/val (cdddr struct-names)]
[struct/c-name/val (add-suffix "/c")]
[struct/dc-name/val (add-suffix "/dc")]
[field-count/val (length selectors/val)]
[f-x/vals (generate-temporaries (syntax (fields ...)))]
[f-xs/vals (generate-arglists f-x/vals)])
(with-syntax ([struct/c struct/c-name/val]
[struct/dc struct/dc-name/val]
[field-count field-count/val]
[(selectors ...) selectors/val]
[struct:-name struct:-name/val]
[struct-maker struct-maker/val]
[predicate predicate/val]
[the-contract (add-suffix "-contract")]
[(selector-indices ...) (nums-up-to field-count/val)]
[(selector-indices+1 ...) (map add1 (nums-up-to field-count/val))]
[(ctc-x ...) (generate-temporaries (syntax (fields ...)))]
[(f-x ...) f-x/vals]
[((f-xs ...) ...) f-xs/vals]
[wrap-name (string->symbol (format "~a/lazy-contract" (syntax-e (syntax name))))]
[opt-wrap-name (string->symbol (format "~a/lazy-opt-contract" (syntax-e (syntax name))))])
#`
(begin
;; `declare' future bindings for the top-level (so that everyone picks them up)
#,@(if (eq? (syntax-local-context) 'top-level)
(list
(syntax
(define-syntaxes (contract-type contract-maker contract-predicate contract-get contract-set
already-there? burrow-in rewrite-fields wrap-get)
(values))))
(list))
(define-syntax name (list #'struct:-name
#'struct-maker
#'predicate
(reverse (list #'selectors ...))
(list #,@(map (λ (x) #f) (syntax->list #'(selectors ...))))
#t))
(define (evaluate-attrs stct contract/info)
(when (wrap-parent-get stct 0) ;; test to make sure this even has attributes
(let* ([any-unknown? #f]
[any-became-known? #f]
[synth-info (wrap-parent-get stct 0)]
[ht (synth-info-vals synth-info)])
(hash-for-each
ht
(lambda (k v)
(when (unknown? v)
(let ([proc (unknown-proc v)])
(let ([new (proc (wrap-get stct selector-indices+1) ...)])
(cond
[(unknown? new)
(set! any-unknown? #t)]
[else
(set! any-became-known? #t)
(hash-set! ht k new)]))))))
(unless any-unknown?
(check-synth-info-test stct synth-info contract/info))
(when any-became-known?
(for-each
(lambda (x) ((evaluate-attr-prop-accessor x) x contract/info))
(synth-info-parents synth-info)))
(unless any-unknown?
(set-synth-info-parents! synth-info '())))))
(define-values (wrap-type wrap-maker wrap-predicate wrap-get wrap-set)
(make-struct-type 'wrap-name
wrap-parent-type ;; super struct
2 ;; field count
(max 0 (- field-count 1)) ;; auto-field-k
#f ;; auto-field-v
(list (cons evaluate-attr-prop evaluate-attrs))
inspector))
(define-values (opt-wrap-type opt-wrap-maker opt-wrap-predicate opt-wrap-get opt-wrap-set)
(make-struct-type 'opt-wrap-name
#f ;; super struct
2 ;; field count
(+ 1 field-count) ;; auto-field-k
#f ;; auto-field-v
'() ;; prop-value-list
inspector))
(define-values (type struct-maker raw-predicate get set)
(make-struct-type 'name
#f ;; super struct
field-count
0 ;; auto-field-k
'() ;; auto-field-v
'() ;; prop-value-list
inspector))
(define (predicate x) (or (raw-predicate x) (opt-wrap-predicate x) (wrap-predicate x)))
(define-syntax (struct/dc stx)
(syntax-case stx ()
[(_ clause (... ...))
(with-syntax ([((maker-args (... ...))
(names (... ...)))
(build-clauses 'struct/dc
(syntax coerce-contract)
stx
(syntax (clause (... ...))))])
(syntax
(let ([names 'names] (... ...))
(contract-maker maker-args (... ...)))))]))
(define (do-selection stct i+1)
(let-values ([(stct fields ...)
(let loop ([stct stct])
(cond
[(raw-predicate stct)
;; found the original value
(values #f (get stct selector-indices) ...)]
;; main : syntax syntax[list-of-identifier] syntax boolean -> syntax
;; define-struct? tells us if this is a 'contract-struct' or a 'define-contract-struct'
(define-for-syntax (main stx name fields inspector define-struct?)
(with-syntax ([name name]
[(fields ...) fields]
[inspector inspector])
(let* ([mutable? (syntax-e (syntax mutable?))]
[add-suffix
(λ (suffix)
(datum->syntax (syntax name)
(string->symbol
(string-append (symbol->string (syntax-e (syntax name)))
suffix))
stx))]
[struct-names (build-struct-names (syntax name)
(syntax->list (syntax (fields ...)))
#f
#t
stx)]
[struct:-name/val (list-ref struct-names 0)]
[struct-maker/val (list-ref struct-names 1)]
[predicate/val (list-ref struct-names 2)]
[selectors/val (cdddr struct-names)]
[struct/c-name/val (add-suffix "/c")]
[struct/dc-name/val (add-suffix "/dc")]
[field-count/val (length selectors/val)]
[f-x/vals (generate-temporaries (syntax (fields ...)))]
[f-xs/vals (generate-arglists f-x/vals)])
(with-syntax ([struct/c struct/c-name/val]
[struct/dc struct/dc-name/val]
[field-count field-count/val]
[(selectors ...) selectors/val]
[struct:-name struct:-name/val]
[struct-maker struct-maker/val]
[predicate predicate/val]
[the-contract (add-suffix "-contract")]
[(selector-indices ...) (nums-up-to field-count/val)]
[(selector-indices+1 ...) (map add1 (nums-up-to field-count/val))]
[(ctc-x ...) (generate-temporaries (syntax (fields ...)))]
[(f-x ...) f-x/vals]
[((f-xs ...) ...) f-xs/vals]
[wrap-name (string->symbol (format "~a/lazy-contract" (syntax-e (syntax name))))]
[opt-wrap-name (string->symbol (format "~a/lazy-opt-contract" (syntax-e (syntax name))))])
#`
(begin
;; `declare' future bindings for the top-level (so that everyone picks them up)
#,@(if (eq? (syntax-local-context) 'top-level)
(list
(syntax
(define-syntaxes (contract-type contract-maker contract-predicate contract-get contract-set
already-there? burrow-in rewrite-fields wrap-get)
(values))))
(list))
(define-syntax name
#,(let ([the-info-list
#`(list #'struct:-name
#'struct-maker
#'predicate
(reverse (list #'selectors ...))
(list #,@(map (λ (x) #f) (syntax->list #'(selectors ...))))
#t)])
(if define-struct?
the-info-list
#`(contract-struct-transformer #,the-info-list
(λ (stx)
(if (identifier? stx)
#'struct-maker
(let ([pr (syntax-e stx)])
(if (pair? pr)
(datum->syntax #'here (cons #'struct-maker (cdr pr)))
(raise-syntax-error #f "what happened?" stx)))))))))
(define (evaluate-attrs stct contract/info)
(when (wrap-parent-get stct 0) ;; test to make sure this even has attributes
(let* ([any-unknown? #f]
[any-became-known? #f]
[synth-info (wrap-parent-get stct 0)]
[ht (synth-info-vals synth-info)])
(hash-for-each
ht
(lambda (k v)
(when (unknown? v)
(let ([proc (unknown-proc v)])
(let ([new (proc (wrap-get stct selector-indices+1) ...)])
(cond
[(unknown? new)
(set! any-unknown? #t)]
[else
(set! any-became-known? #t)
(hash-set! ht k new)]))))))
(unless any-unknown?
(check-synth-info-test stct synth-info contract/info))
(when any-became-known?
(for-each
(lambda (x) ((evaluate-attr-prop-accessor x) x contract/info))
(synth-info-parents synth-info)))
(unless any-unknown?
(set-synth-info-parents! synth-info '())))))
(define-values (wrap-type wrap-maker wrap-predicate wrap-get wrap-set)
(make-struct-type 'wrap-name
wrap-parent-type ;; super struct
2 ;; field count
(max 0 (- field-count 1)) ;; auto-field-k
#f ;; auto-field-v
(list (cons evaluate-attr-prop evaluate-attrs))
inspector))
(define-values (opt-wrap-type opt-wrap-maker opt-wrap-predicate opt-wrap-get opt-wrap-set)
(make-struct-type 'opt-wrap-name
#f ;; super struct
2 ;; field count
(+ 1 field-count) ;; auto-field-k
#f ;; auto-field-v
'() ;; prop-value-list
inspector))
(define-values (type struct-maker raw-predicate get set)
(make-struct-type 'name
#f ;; super struct
field-count
0 ;; auto-field-k
'() ;; auto-field-v
'() ;; prop-value-list
inspector))
(define (predicate x) (or (raw-predicate x) (opt-wrap-predicate x) (wrap-predicate x)))
(define-syntax (struct/dc stx)
(syntax-case stx ()
[(_ clause (... ...))
(with-syntax ([((maker-args (... ...))
(names (... ...)))
(build-clauses 'struct/dc
(syntax coerce-contract)
stx
(syntax (clause (... ...))))])
(syntax
(let ([names 'names] (... ...))
(contract-maker maker-args (... ...)))))]))
(define (do-selection stct i+1)
(let-values ([(stct fields ...)
(let loop ([stct stct])
(cond
[(raw-predicate stct)
;; found the original value
(values #f (get stct selector-indices) ...)]
[(opt-wrap-predicate stct)
(let ((inner (opt-wrap-get stct 0)))
(if inner
(let* ((info (opt-wrap-get stct 1))
(enforcer (opt-contract/info-enforcer info)))
(let-values ([(inner-stct fields ...) (loop inner)])
(let-values ([(fields ...) (enforcer stct fields ...)])
(opt-wrap-set stct 0 #f)
(opt-wrap-set stct selector-indices+1 fields) ...
(values stct fields ...))))
;; found a cached version
(values #f (opt-wrap-get stct selector-indices+1) ...)))]
[(wrap-predicate stct)
(let ([inner (wrap-get stct 0)])
(if inner
;; we have a contract to update
(let ([contract/info (wrap-get stct 1)])
(let-values ([(_1 fields ...) (loop inner)])
(let-values ([(fields ...)
(rewrite-fields stct contract/info fields ...)])
(wrap-set stct 0 #f)
(wrap-set stct selector-indices+1 fields) ...
(evaluate-attrs stct contract/info)
(values stct fields ...))))
;; found a cached version of the value
(values #f (wrap-get stct selector-indices+1) ...)))]))])
(cond
[(opt-wrap-predicate stct) (opt-wrap-get stct i+1)]
[(wrap-predicate stct) (wrap-get stct i+1)])))
(define (rewrite-fields parent contract/info ctc-x ...)
(let* ([f-x (let* ([ctc-field (contract-get (contract/info-contract contract/info)
selector-indices)]
[ctc (if (contract-struct? ctc-field)
ctc-field
(ctc-field f-xs ...))]
[(opt-wrap-predicate stct)
(let ((inner (opt-wrap-get stct 0)))
(if inner
(let* ((info (opt-wrap-get stct 1))
(enforcer (opt-contract/info-enforcer info)))
(let-values ([(inner-stct fields ...) (loop inner)])
(let-values ([(fields ...) (enforcer stct fields ...)])
(opt-wrap-set stct 0 #f)
(opt-wrap-set stct selector-indices+1 fields) ...
(values stct fields ...))))
;; found a cached version
(values #f (opt-wrap-get stct selector-indices+1) ...)))]
[(wrap-predicate stct)
(let ([inner (wrap-get stct 0)])
(if inner
;; we have a contract to update
(let ([contract/info (wrap-get stct 1)])
(let-values ([(_1 fields ...) (loop inner)])
(let-values ([(fields ...)
(rewrite-fields stct contract/info fields ...)])
(wrap-set stct 0 #f)
(wrap-set stct selector-indices+1 fields) ...
(evaluate-attrs stct contract/info)
(values stct fields ...))))
;; found a cached version of the value
(values #f (wrap-get stct selector-indices+1) ...)))]))])
(cond
[(opt-wrap-predicate stct) (opt-wrap-get stct i+1)]
[(wrap-predicate stct) (wrap-get stct i+1)])))
(define (rewrite-fields parent contract/info ctc-x ...)
(let* ([f-x (let* ([ctc-field (contract-get (contract/info-contract contract/info)
selector-indices)]
[ctc (if (contract-struct? ctc-field)
ctc-field
(ctc-field f-xs ...))]
[ctc-field-val
(((contract-projection ctc)
(contract/info-blame contract/info))
ctc-x)])
(update-parent-links parent ctc-field-val)
ctc-field-val)] ...)
(values f-x ...)))
(define (stronger-lazy-contract? a b)
(and (contract-predicate b)
(contract-stronger?
(contract-get a selector-indices)
(contract-get b selector-indices)) ...))
(define (lazy-contract-proj ctc)
(λ (blame)
(let ([contract/info (make-contract/info ctc blame)])
(λ (val)
(unless (or (wrap-predicate val)
(opt-wrap-predicate val)
(raw-predicate val))
(raise-blame-error
blame
val
"expected <~a>, got ~e" 'name val))
(cond
[(already-there? contract/info val lazy-depth-to-look)
val]
[else
(let ([wrapper (wrap-maker val contract/info)])
(let ([synth-setup-stuff (contract-get ctc field-count)])
(when synth-setup-stuff
(let ([ht (make-hash)])
(for-each (λ (pr) (hash-set! ht (car pr) (make-unknown (cdr pr))))
(cdr synth-setup-stuff))
(wrap-parent-set wrapper 0 (make-synth-info '() ht (car synth-setup-stuff))))))
wrapper)])))))
(define (already-there? new-contract/info val depth)
(cond
[(raw-predicate val) #f]
[(zero? depth) #f]
[(wrap-predicate val)
(and (wrap-get val 0)
(let ([old-contract/info (wrap-get val 1)])
(if (and (equal? (contract/info-blame new-contract/info)
(contract/info-blame old-contract/info))
(contract-stronger? (contract/info-contract old-contract/info)
(contract/info-contract new-contract/info)))
#t
(already-there? new-contract/info (wrap-get val 0) (- depth 1)))))]
[else
;; when the zeroth field is cleared out, we don't
;; have a contract to compare to anymore.
#f]))
(define (struct/c ctc-x ...)
(let ([ctc-x (coerce-contract 'struct/c ctc-x)] ...)
(contract-maker ctc-x ... #f)))
(define (selectors x)
(burrow-in x 'selectors selector-indices))
...
(define (burrow-in struct selector-name i)
(cond
[ctc-field-val
(((contract-projection ctc)
(contract/info-blame contract/info))
ctc-x)])
(update-parent-links parent ctc-field-val)
ctc-field-val)] ...)
(values f-x ...)))
(define (stronger-lazy-contract? a b)
(and (contract-predicate b)
(contract-stronger?
(contract-get a selector-indices)
(contract-get b selector-indices)) ...))
(define (lazy-contract-proj ctc)
(λ (blame)
(let ([contract/info (make-contract/info ctc blame)])
(λ (val)
(unless (or (wrap-predicate val)
(opt-wrap-predicate val)
(raw-predicate val))
(raise-blame-error
blame
val
"expected <~a>, got ~e" 'name val))
(cond
[(already-there? contract/info val lazy-depth-to-look)
val]
[else
(let ([wrapper (wrap-maker val contract/info)])
(let ([synth-setup-stuff (contract-get ctc field-count)])
(when synth-setup-stuff
(let ([ht (make-hash)])
(for-each (λ (pr) (hash-set! ht (car pr) (make-unknown (cdr pr))))
(cdr synth-setup-stuff))
(wrap-parent-set wrapper 0 (make-synth-info '() ht (car synth-setup-stuff))))))
wrapper)])))))
(define (already-there? new-contract/info val depth)
(cond
[(raw-predicate val) #f]
[(zero? depth) #f]
[(wrap-predicate val)
(and (wrap-get val 0)
(let ([old-contract/info (wrap-get val 1)])
(if (and (equal? (contract/info-blame new-contract/info)
(contract/info-blame old-contract/info))
(contract-stronger? (contract/info-contract old-contract/info)
(contract/info-contract new-contract/info)))
#t
(already-there? new-contract/info (wrap-get val 0) (- depth 1)))))]
[else
;; when the zeroth field is cleared out, we don't
;; have a contract to compare to anymore.
#f]))
(define (struct/c ctc-x ...)
(let ([ctc-x (coerce-contract 'struct/c ctc-x)] ...)
(contract-maker ctc-x ... #f)))
(define (selectors x)
(burrow-in x 'selectors selector-indices))
...
(define (burrow-in struct selector-name i)
(cond
[(raw-predicate struct)
(get struct i)]
[(opt-wrap-predicate struct)
(if (opt-wrap-get struct 0)
(do-selection struct (+ i 1))
(opt-wrap-get struct (+ i 1)))]
(do-selection struct (+ i 1))
(opt-wrap-get struct (+ i 1)))]
[(wrap-predicate struct)
(if (wrap-get struct 0)
(do-selection struct (+ i 1))
(wrap-get struct (+ i 1)))]
(do-selection struct (+ i 1))
(wrap-get struct (+ i 1)))]
[else
(error selector-name "expected <~a>, got ~e" 'name struct)]))
(define (lazy-contract-name ctc)
(do-contract-name 'struct/c
'struct/dc
(list (contract-get ctc selector-indices) ...)
'(fields ...)
(contract-get ctc field-count)))
(define lazy-contract-property
(build-contract-property
#:projection lazy-contract-proj
#:name lazy-contract-name
#:first-order (lambda (ctc) predicate)
#:stronger stronger-lazy-contract?))
(define-values (contract-type contract-maker contract-predicate contract-get contract-set)
(make-struct-type 'the-contract
#f
(+ field-count 1) ;; extra field is for synthesized attribute ctcs
;; it is a list whose first element is
;; a procedure (called once the attrs are known) that
;; indicates if the test passes. the rest of the elements are
;; procedures that build the attrs
;; this field is #f when there is no synthesized attrs
0 ;; auto-field-k
'() ;; auto-field-v
(list (cons prop:contract lazy-contract-property))))
(define-for-syntax (build-enforcer opt/i opt/info name stx clauses
helper-id-var helper-info helper-freev
enforcer-id-var)
(define (make-free-vars free-vars freev)
(let loop ([i 0]
[stx null]
[free-vars free-vars])
(cond
(define (lazy-contract-name ctc)
(do-contract-name 'struct/c
'struct/dc
(list (contract-get ctc selector-indices) ...)
'(fields ...)
(contract-get ctc field-count)))
(define lazy-contract-property
(build-contract-property
#:projection lazy-contract-proj
#:name lazy-contract-name
#:first-order (lambda (ctc) predicate)
#:stronger stronger-lazy-contract?))
(define-values (contract-type contract-maker contract-predicate contract-get contract-set)
(make-struct-type 'the-contract
#f
(+ field-count 1) ;; extra field is for synthesized attribute ctcs
;; it is a list whose first element is
;; a procedure (called once the attrs are known) that
;; indicates if the test passes. the rest of the elements are
;; procedures that build the attrs
;; this field is #f when there is no synthesized attrs
0 ;; auto-field-k
'() ;; auto-field-v
(list (cons prop:contract lazy-contract-property))))
(define-for-syntax (build-enforcer opt/i opt/info name stx clauses
helper-id-var helper-info helper-freev
enforcer-id-var)
(define (make-free-vars free-vars freev)
(let loop ([i 0]
[stx null]
[free-vars free-vars])
(cond
[(null? free-vars) (reverse stx)]
[else (loop (+ i 1)
(cons (with-syntax ((var (car free-vars))
@ -337,87 +350,87 @@ it around flattened out.
(j (+ i 2)))
(syntax (var (opt-wrap-get stct j)))) stx)
(cdr free-vars))])))
(let*-values ([(inner-val) #'val]
[(clauses lifts superlifts stronger-ribs)
(build-enforcer-clauses opt/i
(opt/info-change-val inner-val opt/info)
name
stx
clauses
(list (syntax f-x) ...)
(list (list (syntax f-xs) ...) ...)
helper-id-var
helper-info
helper-freev)])
(with-syntax ([(clause (... ...)) clauses]
[enforcer-id enforcer-id-var]
[helper-id helper-id-var]
[((free-var free-var-val) (... ...))
(make-free-vars (append (opt/info-free-vars opt/info)) #'freev)]
[(saved-lifts (... ...)) (lifts-to-save lifts)])
(values
#`(λ (stct f-x ...)
(let ((free-var free-var-val) (... ...))
#,(bind-lifts
lifts
#'(let* (clause (... ...))
(values f-x ...)))))
lifts
superlifts
stronger-ribs))))
;;
;; struct/dc opter
;;
(define/opter (struct/dc opt/i opt/info stx)
(syntax-case stx ()
[(_ clause (... ...))
(let ((enforcer-id-var (car (generate-temporaries (syntax (enforcer)))))
(helper-id-var (car (generate-temporaries (syntax (helper)))))
(contract/info-var (car (generate-temporaries (syntax (contract/info)))))
(id-var (car (generate-temporaries (syntax (id))))))
(let-values ([(enforcer lifts superlifts stronger-ribs)
(build-enforcer opt/i
opt/info
'struct/dc
stx
(syntax (clause (... ...)))
helper-id-var
#'info
#'freev
enforcer-id-var)])
(let ([to-save (append (opt/info-free-vars opt/info)
(lifts-to-save lifts))])
(with-syntax ((val (opt/info-val opt/info))
(blame (opt/info-blame opt/info))
(ctc (opt/info-contract opt/info))
(enforcer-id enforcer-id-var)
(helper-id helper-id-var)
(contract/info contract/info-var)
(id id-var)
((j (... ...)) (let loop ([i 2]
[lst to-save])
(cond
(let*-values ([(inner-val) #'val]
[(clauses lifts superlifts stronger-ribs)
(build-enforcer-clauses opt/i
(opt/info-change-val inner-val opt/info)
name
stx
clauses
(list (syntax f-x) ...)
(list (list (syntax f-xs) ...) ...)
helper-id-var
helper-info
helper-freev)])
(with-syntax ([(clause (... ...)) clauses]
[enforcer-id enforcer-id-var]
[helper-id helper-id-var]
[((free-var free-var-val) (... ...))
(make-free-vars (append (opt/info-free-vars opt/info)) #'freev)]
[(saved-lifts (... ...)) (lifts-to-save lifts)])
(values
#`(λ (stct f-x ...)
(let ((free-var free-var-val) (... ...))
#,(bind-lifts
lifts
#'(let* (clause (... ...))
(values f-x ...)))))
lifts
superlifts
stronger-ribs))))
;;
;; struct/dc opter
;;
(define/opter (struct/dc opt/i opt/info stx)
(syntax-case stx ()
[(_ clause (... ...))
(let ((enforcer-id-var (car (generate-temporaries (syntax (enforcer)))))
(helper-id-var (car (generate-temporaries (syntax (helper)))))
(contract/info-var (car (generate-temporaries (syntax (contract/info)))))
(id-var (car (generate-temporaries (syntax (id))))))
(let-values ([(enforcer lifts superlifts stronger-ribs)
(build-enforcer opt/i
opt/info
'struct/dc
stx
(syntax (clause (... ...)))
helper-id-var
#'info
#'freev
enforcer-id-var)])
(let ([to-save (append (opt/info-free-vars opt/info)
(lifts-to-save lifts))])
(with-syntax ((val (opt/info-val opt/info))
(blame (opt/info-blame opt/info))
(ctc (opt/info-contract opt/info))
(enforcer-id enforcer-id-var)
(helper-id helper-id-var)
(contract/info contract/info-var)
(id id-var)
((j (... ...)) (let loop ([i 2]
[lst to-save])
(cond
[(null? lst) null]
[else (cons i (loop (+ i 1) (cdr lst)))])))
((free-var (... ...)) to-save))
(with-syntax ([(stronger-this-var (... ...)) (map stronger-rib-this-var stronger-ribs)]
[(stronger-that-var (... ...)) (map stronger-rib-that-var stronger-ribs)]
[(stronger-exps (... ...)) (map stronger-rib-stronger-exp stronger-ribs)]
[(stronger-indexes (... ...)) (build-list (length stronger-ribs)
(λ (x) (+ x 2)))]
[(stronger-var (... ...)) (map stronger-rib-save-id stronger-ribs)])
(let ([partials
(list (cons id-var #'(begin-lifted (box 'identity)))
(cons enforcer-id-var enforcer)
(cons contract/info-var
(syntax
(make-opt-contract/info ctc enforcer-id id))))])
(values
(syntax
(cond
((free-var (... ...)) to-save))
(with-syntax ([(stronger-this-var (... ...)) (map stronger-rib-this-var stronger-ribs)]
[(stronger-that-var (... ...)) (map stronger-rib-that-var stronger-ribs)]
[(stronger-exps (... ...)) (map stronger-rib-stronger-exp stronger-ribs)]
[(stronger-indexes (... ...)) (build-list (length stronger-ribs)
(λ (x) (+ x 2)))]
[(stronger-var (... ...)) (map stronger-rib-save-id stronger-ribs)])
(let ([partials
(list (cons id-var #'(begin-lifted (box 'identity)))
(cons enforcer-id-var enforcer)
(cons contract/info-var
(syntax
(make-opt-contract/info ctc enforcer-id id))))])
(values
(syntax
(cond
[(opt-wrap-predicate val)
(if (and (opt-wrap-get val 0)
(let ([stronger-this-var stronger-var]
@ -433,10 +446,10 @@ it around flattened out.
;; the rest of this test is bogus and may fail at runtime
(eq? id (opt-contract/info-id (opt-wrap-get val 1)))
stronger-exps (... ...))))
val
(let ([w (opt-wrap-maker val contract/info)])
(opt-wrap-set w j free-var) (... ...)
w))]
val
(let ([w (opt-wrap-maker val contract/info)])
(opt-wrap-set w j free-var) (... ...)
w))]
[(or (raw-predicate val)
(wrap-predicate val))
(let ([w (opt-wrap-maker val contract/info)])
@ -449,13 +462,29 @@ it around flattened out.
"expected <~a>, got ~e"
(contract-name ctc)
val)]))
lifts
superlifts
partials
#f
#f
stronger-ribs)))))))]))
)))]))
lifts
superlifts
partials
#f
#f
stronger-ribs)))))))]))
)))))
(define-syntax (define-contract-struct stx)
(syntax-case stx ()
[(_ name (fields ...))
(syntax (define-contract-struct name (fields ...) (current-inspector)))]
[(_ name (fields ...) inspector)
(and (identifier? (syntax name))
(andmap identifier? (syntax->list (syntax (fields ...)))))
(main stx #'name #'(fields ...) #'inspector #t)]))
(define-syntax (contract-struct stx)
(syntax-case stx()
[(_ name (fields ...))
#'(contract-struct name (fields ...) #:inspector (current-inspector))]
[(_ name (fields ...) #:inspector e)
(main stx #'name #'(fields ...) #'e #f)]))
(define (do-contract-name name/c name/dc list-of-subcontracts fields attrs)
(cond

View File

@ -1,5 +1,5 @@
#lang scribble/doc
@(require "mz.ss")
@(require "mz.rkt")
@(require (for-label syntax/modcollapse))
@(define contract-eval
@ -710,18 +710,17 @@ be blamed using the above contract:
@section{Lazy Data-structure Contracts}
@defform[
(define-contract-struct id (field-id ...))
]{
@defform[(contract-struct id (field-id ...))]{
Like @racket[define-struct], but with two differences: it does not
define field mutators, and it does define two contract constructors:
Like @racket[struct], but with two differences:
they do not
define field mutators, and the do define two contract constructors:
@racket[id]@racketidfont{/c} and @racket[id]@racketidfont{/dc}. The
first is a procedure that accepts as many arguments as there are
fields and returns a contract for struct values whose fields match the
arguments. The second is a syntactic form that also produces contracts
on the structs, but the contracts on later fields may depend on the
values of earlier fields.
values of earlier fields.
The generated contract combinators are @italic{lazy}: they only verify
the contract holds for the portion of some data structure that is
@ -739,21 +738,26 @@ not checked until a selector extracts a field of a struct.
In each @racket[field-spec] case, the first @racket[field-id]
specifies which field the contract applies to; the fields must be
specified in the same order as the original
@racket[define-contract-struct]. The first case is for when the
@racket[contract-struct]. The first case is for when the
contract on the field does not depend on the value of any other
field. The second case is for when the contract on the field does
depend on some other fields, and the parenthesized @racket[field-id]s
indicate which fields it depends on; these dependencies can only be to
earlier fields.}
earlier fields.}}
@defform[(define-contract-struct id (field-id ...))]{
Like @racket[contract-struct], but where the maker's name is @racketidfont["make-"]@racket[id],
much like @racket[define-struct].
}
As an example, consider the following module:
As an example of lazy contract checking, consider the following module:
@(begin
#reader scribble/comment-reader
[racketmod
racket
(define-contract-struct kons (hd tl))
(contract-struct kons (hd tl))
;; @racket[sorted-list/gt : number -> contract]
;; produces a contract that accepts
@ -778,7 +782,7 @@ racket
(* (kons-hd l)
(product (kons-tl l))))]))
(provide kons? make-kons kons-hd kons-tl)
(provide kons? kons kons-hd kons-tl)
(provide/contract [product (-> (sorted-list/gt -inf.0) number?)])
])

View File

@ -7941,6 +7941,16 @@
[tl (hd) (-> (>=/c hd) (>=/c hd))])])
((couple-tl (contract c x 'pos 'neg)) -11)))
(contract-eval '(contract-struct no-define (x)))
(test/spec-passed/result
'd-c-s43
'(no-define-x (no-define 1))
'1)
(test/spec-passed/result
'd-c-s44
'(no-define? (no-define 1))
'#t)
;
;