port if/c to late-neg, add tests, and fix some (minor) bugs

This commit is contained in:
Robby Findler 2015-12-21 09:20:09 -06:00
parent 8776ab7686
commit e4ffa6c97c
4 changed files with 153 additions and 38 deletions

View File

@ -71,6 +71,9 @@
(test-flat-contract #rx#".x." "axq" "x")
(test-flat-contract ''() '() #f)
(test-flat-contract '(if/c integer? even? list?) 2 3)
(test-flat-contract '(if/c integer? even? list?) '() #f)
(test/spec-passed 'any/c '(contract any/c 1 'pos 'neg))
(test-flat-contract 'printable/c (vector (cons 1 (box #f))) (lambda (x) x))
(let ()

View File

@ -0,0 +1,85 @@
#lang racket/base
(require "test-util.rkt")
(parameterize ([current-contract-namespace
(make-basic-contract-namespace)])
(test/spec-passed/result
'if/c1
'(contract (if/c integer? even? (listof number?))
2
'pos 'neg)
2)
(test/spec-passed/result
'if/c2
'(contract (if/c integer? even? (listof number?))
'()
'pos 'neg)
'())
(test/pos-blame
'if/c3
'(contract (if/c integer? even? (listof number?))
3
'pos 'neg))
(test/pos-blame
'if/c4
'(contract (if/c integer? even? (listof number?))
'(#f)
'pos 'neg))
(test/pos-blame
'if/c5
'(contract (if/c integer? even? (listof number?))
#f
'pos 'neg))
(test/pos-blame
'if/c6
'(contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1)))
(-> integer? integer?)
number?)
#f
'pos 'neg))
(test/neg-blame
'if/c7
'((contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1)))
(-> integer? integer?)
number?)
(λ (x) x)
'pos 'neg)
#f))
(test/spec-passed/result
'if/c8
'(contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1)))
(-> integer? integer?)
number?)
1
'pos 'neg)
1)
(test/spec-passed/result
'if/c9
'(let ([f (λ (x) x)])
(chaperone-of?
(contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1)))
(-> integer? integer?)
number?)
f
'pos 'neg)
f))
#t)
(test/spec-passed/result
'if/c10
'(chaperone-contract?
(if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1)))
(-> integer? integer?)
number?))
#t))

View File

@ -32,6 +32,11 @@
(or/c (-> (>=/c 5) (>=/c 5)) boolean?))
(test-name '(or/c boolean? (-> (>=/c 5) (>=/c 5)))
(or/c boolean? (-> (>=/c 5) (>=/c 5))))
(test-name '(if/c integer? odd? (-> integer? integer?))
(if/c integer? odd? (-> integer? integer?)))
(test-name '(if/c integer? odd? boolean?)
(if/c integer? odd? boolean?))
(test-name '(first-or/c) (first-or/c))
(test-name '(first-or/c integer? gt0?) (first-or/c integer? (let ([gt0? (lambda (x) (> x 0))]) gt0?)))

View File

@ -1999,44 +1999,66 @@
#:stronger stronger?
#:list-contract? (list-contract? ctc))))))
;; (if/c predicate then/c else/c) applies then/c to satisfying
;; predicate, else/c to those that don't.
(define (if/c predicate then/c else/c)
#|
Naive version:
(or/c (and/c predicate then/c)
(and/c (not/c predicate) else/c))
But that applies predicate twice.
|#
(unless (procedure? predicate)
(raise-type-error 'if/c "procedure?" predicate))
(unless (contract? then/c)
(raise-type-error 'if/c "contract?" then/c))
(unless (contract? else/c)
(raise-type-error 'if/c "contract?" else/c))
(let ([then-ctc (coerce-contract 'if/c then/c)]
[else-ctc (coerce-contract 'if/c else/c)])
(define name (build-compound-type-name 'if/c predicate then-ctc else-ctc))
;; Special case: if both flat contracts, make a flat contract.
(if (and (flat-contract? then-ctc)
(flat-contract? else-ctc))
;; flat contract
(let ([then-pred (flat-contract-predicate then-ctc)]
[else-pred (flat-contract-predicate else-ctc)])
(define (pred x)
(if (predicate x) (then-pred x) (else-pred x)))
(flat-named-contract name pred))
;; ho contract
(let ([then-proj (contract-projection then-ctc)]
[then-fo (contract-first-order then-ctc)]
[else-proj (contract-projection else-ctc)]
[else-fo (contract-first-order else-ctc)])
(define ((proj blame) x)
(if (predicate x)
((then-proj blame) x)
((else-proj blame) x)))
(make-contract
#:name name
#:projection proj
#:first-order
(lambda (x) (if (predicate x) (then-fo x) (else-fo x))))))))
(unless (procedure-arity-includes? predicate 1)
(raise-type-error 'if/c "procedure that accepts 1 argument" predicate))
(define then-ctc (coerce-contract 'if/c then/c))
(define else-ctc (coerce-contract 'if/c else/c))
(cond
[(and (flat-contract? then-ctc)
(flat-contract? else-ctc))
(define then-pred (flat-contract-predicate then-ctc))
(define else-pred (flat-contract-predicate else-ctc))
(define name `(if/c ,(object-name predicate)
,(contract-name then-pred)
,(contract-name else-pred)))
(define (pred x)
(if (predicate x) (then-pred x) (else-pred x)))
(flat-named-contract name pred)]
[(and (chaperone-contract? then-ctc)
(chaperone-contract? else-ctc))
(chaperone-if/c predicate then-ctc else-ctc)]
[else
(impersonator-if/c predicate then-ctc else-ctc)]))
(define (if/c-first-order ctc)
(define predicate (base-if/c-predicate ctc))
(define thn (contract-first-order (base-if/c-thn ctc)))
(define els (contract-first-order (base-if/c-els ctc)))
(λ (x) (if (predicate x) (thn x) (els x))))
(define (if/c-name ctc)
(define predicate (base-if/c-predicate ctc))
(define thn (contract-name (base-if/c-thn ctc)))
(define els (contract-name (base-if/c-els ctc)))
`(if/c ,(object-name predicate) ,thn ,els))
(define (if/c-late-neg-proj ctc)
(define predicate (base-if/c-predicate ctc))
(define thn (contract-late-neg-projection (base-if/c-thn ctc)))
(define els (contract-late-neg-projection (base-if/c-els ctc)))
(λ (blame)
(define thn-proj (thn blame))
(define els-proj (els blame))
(λ (val neg-party)
(if (predicate val)
(thn-proj val neg-party)
(els-proj val neg-party)))))
(define-struct base-if/c (predicate thn els)
#:property prop:custom-write custom-write-property-proc)
(define-struct (chaperone-if/c base-if/c) ()
#:property prop:chaperone-contract
(build-chaperone-contract-property
#:late-neg-projection if/c-late-neg-proj
#:first-order if/c-first-order
#:name if/c-name))
(define-struct (impersonator-if/c base-if/c) ()
#:property prop:contract
(build-contract-property
#:late-neg-projection if/c-late-neg-proj
#:first-order if/c-first-order
#:name if/c-name))