added exists to provide/contract
svn: r15871
This commit is contained in:
parent
5d000d425e
commit
937d2efab3
|
@ -15,7 +15,8 @@ improve method arity mismatch contract violation error messages?
|
||||||
define-struct/contract
|
define-struct/contract
|
||||||
define/contract
|
define/contract
|
||||||
with-contract
|
with-contract
|
||||||
current-contract-region)
|
current-contract-region
|
||||||
|
new-∃/c)
|
||||||
|
|
||||||
(require (for-syntax scheme/base)
|
(require (for-syntax scheme/base)
|
||||||
(for-syntax "contract-opt-guts.ss")
|
(for-syntax "contract-opt-guts.ss")
|
||||||
|
@ -683,9 +684,6 @@ improve method arity mismatch contract violation error messages?
|
||||||
(quasisyntax/loc stx (#%expression #,stx)))))))
|
(quasisyntax/loc stx (#%expression #,stx)))))))
|
||||||
|
|
||||||
|
|
||||||
;; (provide/contract p/c-ele ...)
|
|
||||||
;; p/c-ele = (id expr) | (rename id id expr) | (struct id (id expr) ...)
|
|
||||||
;; provides each `id' with the contract `expr'.
|
|
||||||
(define-syntax (provide/contract provide-stx)
|
(define-syntax (provide/contract provide-stx)
|
||||||
(syntax-case provide-stx (struct)
|
(syntax-case provide-stx (struct)
|
||||||
[(_ p/c-ele ...)
|
[(_ p/c-ele ...)
|
||||||
|
@ -725,19 +723,62 @@ improve method arity mismatch contract violation error messages?
|
||||||
;; code-for-each-clause : (listof syntax) -> (listof syntax)
|
;; code-for-each-clause : (listof syntax) -> (listof syntax)
|
||||||
;; constructs code for each clause of a provide/contract
|
;; constructs code for each clause of a provide/contract
|
||||||
(define (code-for-each-clause clauses)
|
(define (code-for-each-clause clauses)
|
||||||
(cond
|
(let loop ([clauses clauses]
|
||||||
|
[exists-binders '()])
|
||||||
|
(cond
|
||||||
[(null? clauses) null]
|
[(null? clauses) null]
|
||||||
[else
|
[else
|
||||||
(let ([clause (car clauses)])
|
(let ([clause (car clauses)])
|
||||||
;; compare raw identifiers for `struct' and `rename' just like provide does
|
;; compare raw identifiers for `struct' and `rename' just like provide does
|
||||||
(syntax-case* clause (struct rename) (λ (x y) (eq? (syntax-e x) (syntax-e y)))
|
(syntax-case* clause (struct rename) (λ (x y) (eq? (syntax-e x) (syntax-e y)))
|
||||||
|
[exists
|
||||||
|
(or (eq? '#:exists (syntax-e #'exists)) (eq? '#:∃ (syntax-e #'exists)))
|
||||||
|
(cond
|
||||||
|
[(null? (cdr clauses))
|
||||||
|
(raise-syntax-error 'provide/conract
|
||||||
|
(format "expected either a single variable or a sequence of variables to follow ~a, but found nothing"
|
||||||
|
(syntax-e #'exists))
|
||||||
|
provide-stx
|
||||||
|
clause)]
|
||||||
|
[else
|
||||||
|
(syntax-case (cadr clauses) ()
|
||||||
|
[x
|
||||||
|
(identifier? #'x)
|
||||||
|
(with-syntax ([(x-gen) (generate-temporaries #'(x))])
|
||||||
|
(cons (code-for-one-exists-id #'x #'x-gen)
|
||||||
|
(loop (cddr clauses)
|
||||||
|
(add-a-binder #'x #'x-gen exists-binders))))]
|
||||||
|
[(x ...)
|
||||||
|
(andmap identifier? (syntax->list #'(x ...)))
|
||||||
|
(with-syntax ([(x-gen ...) (generate-temporaries #'(x ...))])
|
||||||
|
(append (map code-for-one-exists-id
|
||||||
|
(syntax->list #'(x ...))
|
||||||
|
(syntax->list #'(x-gen ...)))
|
||||||
|
(loop (cddr clauses)
|
||||||
|
(let loop ([binders exists-binders]
|
||||||
|
[xs (syntax->list #'(x ...))]
|
||||||
|
[x-gens (syntax->list #'(x-gen ...))])
|
||||||
|
(cond
|
||||||
|
[(null? xs) binders]
|
||||||
|
[else (loop (add-a-binder (car xs) (car x-gens) binders)
|
||||||
|
(cdr xs)
|
||||||
|
(cdr x-gens))])))))]
|
||||||
|
[else
|
||||||
|
(raise-syntax-error 'provide/contract
|
||||||
|
(format "expected either a single variable or a sequence of variables to follow ~a"
|
||||||
|
(syntax-e #'exists))
|
||||||
|
provide-stx
|
||||||
|
(cadr clauses))])])]
|
||||||
[(rename this-name new-name contract)
|
[(rename this-name new-name contract)
|
||||||
(and (identifier? (syntax this-name))
|
(and (identifier? (syntax this-name))
|
||||||
(identifier? (syntax new-name)))
|
(identifier? (syntax new-name)))
|
||||||
(begin
|
(begin
|
||||||
(add-to-dups-table #'new-name)
|
(add-to-dups-table #'new-name)
|
||||||
(cons (code-for-one-id provide-stx (syntax this-name) (syntax contract) (syntax new-name))
|
(cons (code-for-one-id provide-stx
|
||||||
(code-for-each-clause (cdr clauses))))]
|
(syntax this-name)
|
||||||
|
(add-exists-binders (syntax contract) exists-binders)
|
||||||
|
(syntax new-name))
|
||||||
|
(loop (cdr clauses) exists-binders)))]
|
||||||
[(rename this-name new-name contract)
|
[(rename this-name new-name contract)
|
||||||
(identifier? (syntax this-name))
|
(identifier? (syntax this-name))
|
||||||
(raise-syntax-error 'provide/contract
|
(raise-syntax-error 'provide/contract
|
||||||
|
@ -758,9 +799,10 @@ improve method arity mismatch contract violation error messages?
|
||||||
(let ([sc (build-struct-code provide-stx
|
(let ([sc (build-struct-code provide-stx
|
||||||
(syntax struct-name)
|
(syntax struct-name)
|
||||||
(syntax->list (syntax (field-name ...)))
|
(syntax->list (syntax (field-name ...)))
|
||||||
(syntax->list (syntax (contract ...))))])
|
(map (λ (x) (add-exists-binders x exists-binders))
|
||||||
|
(syntax->list (syntax (contract ...)))))])
|
||||||
(add-to-dups-table #'struct-name)
|
(add-to-dups-table #'struct-name)
|
||||||
(cons sc (code-for-each-clause (cdr clauses))))]
|
(cons sc (loop (cdr clauses) exists-binders)))]
|
||||||
[(struct name)
|
[(struct name)
|
||||||
(identifier? (syntax name))
|
(identifier? (syntax name))
|
||||||
(raise-syntax-error 'provide/contract
|
(raise-syntax-error 'provide/contract
|
||||||
|
@ -801,8 +843,12 @@ improve method arity mismatch contract violation error messages?
|
||||||
(identifier? (syntax name))
|
(identifier? (syntax name))
|
||||||
(begin
|
(begin
|
||||||
(add-to-dups-table #'name)
|
(add-to-dups-table #'name)
|
||||||
(cons (code-for-one-id provide-stx (syntax name) (syntax contract) #f)
|
(cons (code-for-one-id provide-stx
|
||||||
(code-for-each-clause (cdr clauses))))]
|
(syntax name)
|
||||||
|
(add-exists-binders (syntax contract)
|
||||||
|
exists-binders)
|
||||||
|
#f)
|
||||||
|
(loop (cdr clauses) exists-binders)))]
|
||||||
[(name contract)
|
[(name contract)
|
||||||
(raise-syntax-error 'provide/contract
|
(raise-syntax-error 'provide/contract
|
||||||
"expected identifier"
|
"expected identifier"
|
||||||
|
@ -812,7 +858,7 @@ improve method arity mismatch contract violation error messages?
|
||||||
(raise-syntax-error 'provide/contract
|
(raise-syntax-error 'provide/contract
|
||||||
"malformed clause"
|
"malformed clause"
|
||||||
provide-stx
|
provide-stx
|
||||||
(syntax unk))]))]))
|
(syntax unk))]))])))
|
||||||
|
|
||||||
;; well-formed-struct-name? : syntax -> bool
|
;; well-formed-struct-name? : syntax -> bool
|
||||||
(define (well-formed-struct-name? stx)
|
(define (well-formed-struct-name? stx)
|
||||||
|
@ -1159,6 +1205,16 @@ improve method arity mismatch contract violation error messages?
|
||||||
field-contract-id
|
field-contract-id
|
||||||
void?))))
|
void?))))
|
||||||
|
|
||||||
|
;; code-for-one-exists-id : syntax -> syntax
|
||||||
|
(define (code-for-one-exists-id x x-gen)
|
||||||
|
#`(define #,x-gen (new-∃/c '#,x)))
|
||||||
|
|
||||||
|
(define (add-exists-binders stx exists-binders)
|
||||||
|
#`(let #,exists-binders #,stx))
|
||||||
|
|
||||||
|
(define (add-a-binder id id-gen binders)
|
||||||
|
(cons #`[#,id #,id-gen] binders))
|
||||||
|
|
||||||
;; code-for-one-id : syntax syntax syntax (union syntax #f) -> syntax
|
;; code-for-one-id : syntax syntax syntax (union syntax #f) -> syntax
|
||||||
;; given the syntax for an identifier and a contract,
|
;; given the syntax for an identifier and a contract,
|
||||||
;; builds a begin expression for the entire contract and provide
|
;; builds a begin expression for the entire contract and provide
|
||||||
|
@ -1209,7 +1265,9 @@ improve method arity mismatch contract violation error messages?
|
||||||
|
|
||||||
#,@(if no-need-to-check-ctrct?
|
#,@(if no-need-to-check-ctrct?
|
||||||
(list)
|
(list)
|
||||||
(list #'(define contract-id (verify-contract 'provide/contract ctrct))))
|
(list #'(define contract-id
|
||||||
|
(let ([id ctrct]) ;; let is here to give the right name.
|
||||||
|
(verify-contract 'provide/contract id)))))
|
||||||
(define-syntax id-rename
|
(define-syntax id-rename
|
||||||
(make-provide/contract-transformer (quote-syntax contract-id)
|
(make-provide/contract-transformer (quote-syntax contract-id)
|
||||||
(quote-syntax id)
|
(quote-syntax id)
|
||||||
|
@ -2571,3 +2629,33 @@ improve method arity mismatch contract violation error messages?
|
||||||
#:property stronger-prop
|
#:property stronger-prop
|
||||||
(λ (this that)
|
(λ (this that)
|
||||||
#f))
|
#f))
|
||||||
|
|
||||||
|
(define (∃-proj ctc)
|
||||||
|
(let ([in (∃/c-in ctc)]
|
||||||
|
[out (∃/c-out ctc)]
|
||||||
|
[pred? (∃/c-pred? ctc)])
|
||||||
|
(λ (pos-blame neg-blame src-info orig-str positive-position?)
|
||||||
|
(if positive-position?
|
||||||
|
in
|
||||||
|
(λ (val)
|
||||||
|
(if (pred? val)
|
||||||
|
(out val)
|
||||||
|
(raise-contract-error val src-info pos-blame orig-str
|
||||||
|
"non-polymorphic value: ~e"
|
||||||
|
val)))))))
|
||||||
|
|
||||||
|
(define-struct ∃/c (in out pred? name)
|
||||||
|
#:omit-define-syntaxes
|
||||||
|
#:property proj-prop ∃-proj
|
||||||
|
#:property name-prop (λ (ctc) (∃/c-name ctc))
|
||||||
|
#:property first-order-prop
|
||||||
|
(λ (ctc) (λ (x) #t)) ;; ???
|
||||||
|
|
||||||
|
#:property stronger-prop
|
||||||
|
(λ (this that) #f))
|
||||||
|
|
||||||
|
(define (new-∃/c raw-name)
|
||||||
|
(define name (string->symbol (format "~a/∃" raw-name)))
|
||||||
|
(define-values (struct-type constructor predicate accessor mutator)
|
||||||
|
(make-struct-type name #f 1 0))
|
||||||
|
(make-∃/c constructor (λ (x) (accessor x 0)) predicate raw-name))
|
||||||
|
|
|
@ -11,6 +11,10 @@ another. Programmers specify the behavior of a module exports via
|
||||||
@scheme[provide/contract] and the contract system enforces those
|
@scheme[provide/contract] and the contract system enforces those
|
||||||
constraints.
|
constraints.
|
||||||
|
|
||||||
|
@note-lib[scheme/contract #:use-sources (scheme/private/contract-ds
|
||||||
|
scheme/private/contract
|
||||||
|
scheme/private/contract-guts)]
|
||||||
|
|
||||||
@deftech{Contracts} come in two forms: those constructed by the
|
@deftech{Contracts} come in two forms: those constructed by the
|
||||||
various operations listed in this section of the manual, and various
|
various operations listed in this section of the manual, and various
|
||||||
ordinary Scheme values that double as contracts, including
|
ordinary Scheme values that double as contracts, including
|
||||||
|
@ -34,10 +38,6 @@ failed, and anything else to indicate it passed.}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@note-lib[scheme/contract #:use-sources (scheme/private/contract-ds
|
|
||||||
scheme/private/contract
|
|
||||||
scheme/private/contract-guts)]
|
|
||||||
|
|
||||||
@local-table-of-contents[]
|
@local-table-of-contents[]
|
||||||
|
|
||||||
@; ----------------------------------------
|
@; ----------------------------------------
|
||||||
|
@ -368,6 +368,27 @@ Constructs a contract on a promise. The contract does not force the
|
||||||
promise, but when the promise is forced, the contract checks that the
|
promise, but when the promise is forced, the contract checks that the
|
||||||
result value meets the contract produced by @scheme[expr].}
|
result value meets the contract produced by @scheme[expr].}
|
||||||
|
|
||||||
|
@defproc[(new-∃/c [name symbol?]) contract?]{
|
||||||
|
Constructs a new existential contract.
|
||||||
|
|
||||||
|
Existential contracts accept all values when in positive positions (e.g., function
|
||||||
|
returns) and wraps the value in an opaque struct, hiding the precise value.
|
||||||
|
In negative positions (e.g. function inputs),
|
||||||
|
it accepts only values that were previously accepted in negative positions (by checking
|
||||||
|
for the wrappers).
|
||||||
|
|
||||||
|
For example, this contract:
|
||||||
|
@schemeblock[(let ([a (new-∃/c 'a)])
|
||||||
|
(-> (-> a a)
|
||||||
|
any/c))]
|
||||||
|
describes a function that accepts the identity function (or a non-terminating function)
|
||||||
|
and returns an arbitrary value. That is, the first use of the @scheme[a] appears in a
|
||||||
|
positive position and thus inputs to that function are wrapped with an opaque struct.
|
||||||
|
Then, when the function returns, it is checked to see if the result is wrapped, since
|
||||||
|
the second @scheme[a] appears in a negative position.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@; ------------------------------------------------------------------------
|
@; ------------------------------------------------------------------------
|
||||||
|
|
||||||
@section{Function Contracts}
|
@section{Function Contracts}
|
||||||
|
@ -669,7 +690,11 @@ lazy contract.}
|
||||||
(struct id ((id contract-expr) ...))
|
(struct id ((id contract-expr) ...))
|
||||||
(struct (id identifier) ((id contract-expr) ...))
|
(struct (id identifier) ((id contract-expr) ...))
|
||||||
(rename orig-id id contract-expr)
|
(rename orig-id id contract-expr)
|
||||||
(id contract-expr)])]{
|
(id contract-expr)
|
||||||
|
(code:line #:∃ exists-variables)
|
||||||
|
(code:line #:exists exists-variables)]
|
||||||
|
[exists-variables identifier
|
||||||
|
(identifier ...)])]{
|
||||||
|
|
||||||
Can only appear at the top-level of a @scheme[module]. As with
|
Can only appear at the top-level of a @scheme[module]. As with
|
||||||
@scheme[provide], each @scheme[id] is provided from the module. In
|
@scheme[provide], each @scheme[id] is provided from the module. In
|
||||||
|
@ -699,7 +724,13 @@ referring to the parent struct. Unlike @scheme[define-struct],
|
||||||
however, all of the fields (and their contracts) must be listed. The
|
however, all of the fields (and their contracts) must be listed. The
|
||||||
contract on the fields that the sub-struct shares with its parent are
|
contract on the fields that the sub-struct shares with its parent are
|
||||||
only used in the contract for the sub-struct's maker, and the selector
|
only used in the contract for the sub-struct's maker, and the selector
|
||||||
or mutators for the super-struct are not provided.}
|
or mutators for the super-struct are not provided.
|
||||||
|
|
||||||
|
The @scheme[#:∃] and @scheme[#:exists] clauses define new abstract
|
||||||
|
contracts. The variables are bound in the remainder of the @scheme[provide/contract]
|
||||||
|
expression to new contracts that hide the values they accept and
|
||||||
|
ensure that the exported functions are treated parametrically.
|
||||||
|
}
|
||||||
|
|
||||||
@defform/subs[
|
@defform/subs[
|
||||||
(with-contract blame-id (wc-export ...) free-var-list body ...+)
|
(with-contract blame-id (wc-export ...) free-var-list body ...+)
|
||||||
|
|
|
@ -6153,6 +6153,52 @@ so that propagation occurs.
|
||||||
'neg))
|
'neg))
|
||||||
(f 10)))
|
(f 10)))
|
||||||
|
|
||||||
|
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
; ;;;;;;
|
||||||
|
; ; ;
|
||||||
|
; ; ;
|
||||||
|
; ; ;; ;;;;
|
||||||
|
; ;;;;;; ; ;;;;;;
|
||||||
|
; ; ; ;;;
|
||||||
|
; ; ; ;;;
|
||||||
|
; ; ;; ;;;;;;
|
||||||
|
; ;;;;;; ; ;;;;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
|
||||||
|
(test/spec-passed
|
||||||
|
'∃1
|
||||||
|
'(contract (new-∃/c 'pair)
|
||||||
|
1
|
||||||
|
'pos
|
||||||
|
'neg))
|
||||||
|
|
||||||
|
(test/neg-blame
|
||||||
|
'∃2
|
||||||
|
'((contract (-> (new-∃/c 'pair) any/c)
|
||||||
|
(λ (x) x)
|
||||||
|
'pos
|
||||||
|
'neg)
|
||||||
|
1))
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'∃3
|
||||||
|
'(let ([pair (new-∃/c 'pair)])
|
||||||
|
((contract (-> (-> pair pair) any/c)
|
||||||
|
(λ (f) (f 11))
|
||||||
|
'pos
|
||||||
|
'neg)
|
||||||
|
(λ (x) x)))
|
||||||
|
11)
|
||||||
|
|
||||||
|
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
|
@ -6683,6 +6729,104 @@ so that propagation occurs.
|
||||||
(eval 'provide/contract29-res))
|
(eval 'provide/contract29-res))
|
||||||
(list #t 3))
|
(list #t 3))
|
||||||
|
|
||||||
|
;; for this test I have to be able to track back thru the requirees to find the right
|
||||||
|
;; name for the negative blame (currently it blames m3, but it should blame m2).
|
||||||
|
#;
|
||||||
|
(test/spec-failed
|
||||||
|
'provide/contract30
|
||||||
|
'(begin
|
||||||
|
(eval '(module provide/contract30-m1 scheme/base
|
||||||
|
(require scheme/contract)
|
||||||
|
(provide/contract [f (-> integer? integer?)])
|
||||||
|
(define (f x) x)))
|
||||||
|
(eval '(module provide/contract30-m2 scheme/base
|
||||||
|
(require 'provide/contract30-m1)
|
||||||
|
(provide f)))
|
||||||
|
(eval '(module provide/contract30-m3 scheme/base
|
||||||
|
(require 'provide/contract30-m2)
|
||||||
|
(f #f)))
|
||||||
|
(eval '(require 'provide/contract30-m3)))
|
||||||
|
"'provide/contract30-m2")
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'provide/contract31
|
||||||
|
'(begin
|
||||||
|
(eval '(module provide/contract31-m1 scheme/base
|
||||||
|
(require scheme/contract)
|
||||||
|
(provide/contract
|
||||||
|
#:∃ x
|
||||||
|
[f (-> (-> x x) 10)])
|
||||||
|
(define (f g) (g 10))))
|
||||||
|
|
||||||
|
(eval '(module provide/contract31-m2 scheme/base
|
||||||
|
(require scheme/contract 'provide/contract31-m1)
|
||||||
|
(provide provide/contract31-x)
|
||||||
|
(define provide/contract31-x (f (λ (x) x)))))
|
||||||
|
|
||||||
|
(eval '(require 'provide/contract31-m2))
|
||||||
|
(eval 'provide/contract31-x))
|
||||||
|
10)
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'provide/contract32
|
||||||
|
'(begin
|
||||||
|
(eval '(module provide/contract32-m1 scheme/base
|
||||||
|
(require scheme/contract)
|
||||||
|
(provide/contract
|
||||||
|
#:exists x
|
||||||
|
[f (-> (-> x x) 10)])
|
||||||
|
(define (f g) (g 10))))
|
||||||
|
|
||||||
|
(eval '(module provide/contract32-m2 scheme/base
|
||||||
|
(require scheme/contract 'provide/contract32-m1)
|
||||||
|
(provide provide/contract32-x)
|
||||||
|
(define provide/contract32-x (f (λ (x) x)))))
|
||||||
|
|
||||||
|
(eval '(require 'provide/contract32-m2))
|
||||||
|
(eval 'provide/contract32-x))
|
||||||
|
10)
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'provide/contract33
|
||||||
|
'(begin
|
||||||
|
(eval '(module provide/contract33-m1 scheme/base
|
||||||
|
(require scheme/contract)
|
||||||
|
(provide/contract
|
||||||
|
#:exists (x)
|
||||||
|
[f (-> (-> x x) 10)])
|
||||||
|
(define (f g) (g 10))))
|
||||||
|
|
||||||
|
(eval '(module provide/contract33-m2 scheme/base
|
||||||
|
(require scheme/contract 'provide/contract33-m1)
|
||||||
|
(provide provide/contract33-x)
|
||||||
|
(define provide/contract33-x (f (λ (x) x)))))
|
||||||
|
|
||||||
|
(eval '(require 'provide/contract33-m2))
|
||||||
|
(eval 'provide/contract33-x))
|
||||||
|
10)
|
||||||
|
|
||||||
|
(test/spec-passed/result
|
||||||
|
'provide/contract34
|
||||||
|
'(begin
|
||||||
|
(eval '(module provide/contract34-m1 scheme/base
|
||||||
|
(require scheme/contract)
|
||||||
|
(define x integer?)
|
||||||
|
(define g 11)
|
||||||
|
(provide/contract
|
||||||
|
[g x]
|
||||||
|
#:exists (x)
|
||||||
|
[f (-> (-> x x) 10)])
|
||||||
|
(define (f g) (g 10))))
|
||||||
|
|
||||||
|
(eval '(module provide/contract34-m2 scheme/base
|
||||||
|
(require scheme/contract 'provide/contract34-m1)
|
||||||
|
(provide provide/contract34-x)
|
||||||
|
(define provide/contract34-x (f (λ (x) x)))))
|
||||||
|
|
||||||
|
(eval '(require 'provide/contract34-m2))
|
||||||
|
(eval 'provide/contract34-x))
|
||||||
|
10)
|
||||||
|
|
||||||
(contract-error-test
|
(contract-error-test
|
||||||
#'(begin
|
#'(begin
|
||||||
(eval '(module pce1-bug scheme/base
|
(eval '(module pce1-bug scheme/base
|
||||||
|
|
Loading…
Reference in New Issue
Block a user