added forall to go along with exists contracts

This commit is contained in:
Robby Findler 2010-11-11 13:47:31 -06:00
parent bcef0dbfe2
commit 5a9c469b0f
5 changed files with 78 additions and 18 deletions

View File

@ -16,7 +16,7 @@ differences from v3:
"private/define-struct.rkt") "private/define-struct.rkt")
(provide (all-from-out "contract/base.rkt") (provide (all-from-out "contract/base.rkt")
(except-out (all-from-out racket/contract/exists) ∃?) (except-out (all-from-out racket/contract/exists) ∃?)
(all-from-out racket/contract/regions)) (all-from-out racket/contract/regions))
;; ====================================================================== ;; ======================================================================

View File

@ -3,14 +3,16 @@
(require "private/guts.rkt") (require "private/guts.rkt")
(provide new-∃/c (provide new-∃/c
∃?) new-∀/c
∀∃?)
(define (∃-proj ctc) (define (∀∃-proj ctc)
(let ([in (∃/c-in ctc)] (let ([in (∀∃/c-in ctc)]
[out (∃/c-out ctc)] [out (∀∃/c-out ctc)]
[pred? (∃/c-pred? ctc)]) [pred? (∀∃/c-pred? ctc)]
[neg? (∀∃/c-neg? ctc)])
(λ (blame) (λ (blame)
(if (blame-swapped? blame) (if (eq? neg? (blame-swapped? blame))
(λ (val) (λ (val)
(if (pred? val) (if (pred? val)
(out val) (out val)
@ -20,18 +22,22 @@
val))) val)))
in)))) in))))
(define-struct ∃/c (in out pred? name) (define-struct ∃/c (in out pred? name neg?)
#:omit-define-syntaxes #:omit-define-syntaxes
#:property prop:contract #:property prop:contract
(build-contract-property (build-contract-property
#:name (λ (ctc) (∃/c-name ctc)) #:name (λ (ctc) (∃/c-name ctc))
#:first-order (λ (ctc) (λ (x) #t)) ;; ??? #:first-order (λ (ctc) (λ (x) #t)) ;; ???
#:projection ∃-proj)) #:projection ∃-proj))
(define-struct ()) (define-struct ())
(define (new-∃/c raw-name) (define (new-∃/c raw-name) (mk raw-name #t))
(define name (string->symbol (format "~a/∃" raw-name))) (define (new-∀/c raw-name) (mk raw-name #f))
(define (mk raw-name neg?)
(define name (string->symbol (format "~a/~a" raw-name (if neg? "" ""))))
(define-values (struct-type constructor predicate accessor mutator) (define-values (struct-type constructor predicate accessor mutator)
(make-struct-type name struct:∃ 1 0)) (make-struct-type name struct:∀∃ 1 0))
(make-∃/c constructor (λ (x) (accessor x 0)) predicate raw-name)) (make-∀∃/c constructor (λ (x) (accessor x 0)) predicate raw-name neg?))

View File

@ -243,7 +243,7 @@
#'(begin #'(begin
(define -predicates (define -predicates
(let ([predicates (λ (x) (let ([predicates (λ (x)
(if (∃? x) (if (∃? x)
(error 'predicates "supplied with a wrapped value ~e" x) (error 'predicates "supplied with a wrapped value ~e" x)
(predicates x)))]) (predicates x)))])
predicates)) predicates))

View File

@ -424,15 +424,42 @@ 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 @racket[expr].} result value meets the contract produced by @racket[expr].}
@defproc[(new-∀/c [name symbol?]) contract?]{
Constructs a new universal contract.
Universal contracts accept all values when in negative positions (e.g., function
inputs) and wrap them in an opaque struct, hiding the precise value.
In positive positions (e.g. function returns),
a universal contract accepts only values that were previously accepted in negative positions (by checking
for the wrappers).
The name is used to identify the contract in error messages.
For example, this contract:
@racketblock[(let ([a (new-∃/c 'a)])
(-> a a))]
describes the identity function (or a non-terminating function)
That is, the first use of the @racket[a] appears in a
negative 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 @racket[a] appears in a positive position.
This is a dual to @racket[new-∃/c].
}
@defproc[(new-∃/c [name symbol?]) contract?]{ @defproc[(new-∃/c [name symbol?]) contract?]{
Constructs a new existential contract. Constructs a new existential contract.
Existential contracts accept all values when in positive positions (e.g., function 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. returns) and wrap them in an opaque struct, hiding the precise value.
In negative positions (e.g. function inputs), In negative positions (e.g. function inputs),
it accepts only values that were previously accepted in negative positions (by checking they accepts only values that were previously accepted in positive positions (by checking
for the wrappers). for the wrappers).
The name is used to identify the contract in error messages.
For example, this contract: For example, this contract:
@racketblock[(let ([a (new-∃/c 'a)]) @racketblock[(let ([a (new-∃/c 'a)])
(-> (-> a a) (-> (-> a a)
@ -443,8 +470,10 @@ result value meets the contract produced by @racket[expr].}
Then, when the function returns, it is checked to see if the result is wrapped, since Then, when the function returns, it is checked to see if the result is wrapped, since
the second @racket[a] appears in a negative position. the second @racket[a] appears in a negative position.
This is a dual to @racket[new-∀/c].
} }
@; ------------------------------------------------------------------------ @; ------------------------------------------------------------------------
@section{Function Contracts} @section{Function Contracts}

View File

@ -9984,6 +9984,31 @@ so that propagation occurs.
(λ (x) x))) (λ (x) x)))
11) 11)
(test/pos-blame
'∀1
'(contract (new-∀/c 'pair)
1
'pos
'neg))
(test/spec-passed
'∀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)
(λ (x) x)
'pos
'neg)
11))
11)
; ;
; ;
; ;