add support for ... to -> contracts to indicate repeated arguments

also fix order of evaluation for ->
This commit is contained in:
Robby Findler 2016-01-28 15:33:37 -06:00
parent 856e60fe51
commit ec4bd288bf
9 changed files with 282 additions and 71 deletions

View File

@ -1011,13 +1011,18 @@ designed to match @racket[case-lambda] and
without requiring that the domain have any particular shape without requiring that the domain have any particular shape
(see below for an example use). (see below for an example use).
@defform*/subs[#:literals (any values) @(define lit-ellipsis (racket ...))
[(-> dom ... range)]
([dom dom-expr (code:line keyword dom-expr)]
[range range-expr (values range-expr ...) any])]{
Produces a contract for a function that accepts a fixed @defform*/subs[#:literals (any values)
number of arguments and returns either a fixed number of [(-> dom ... range)
(-> dom ... ellipsis dom-expr ... range)]
([dom dom-expr (code:line keyword dom-expr)]
[range range-expr (values range-expr ...) any]
[ellipsis #,lit-ellipsis])]{
Produces a contract for a function that accepts the argument
specified by the @racket[dom-expr] contracts and returns
either a fixed number of
results or completely unspecified results (the latter when results or completely unspecified results (the latter when
@racket[any] is specified). @racket[any] is specified).
@ -1025,6 +1030,13 @@ Each @racket[dom-expr] is a contract on an argument to a
function, and each @racket[range-expr] is a contract on a function, and each @racket[range-expr] is a contract on a
result of the function. result of the function.
If the domain contain @racket[...]
then the function accepts as many arguments as the rest of
the contracts in the domain portion specify, as well as
arbitrarily many more that match the contract just before the
@racket[...]. Otherwise, the contract accepts exactly the
argument specified.
@margin-note{Using a @racket[->] between two whitespace-delimited @margin-note{Using a @racket[->] between two whitespace-delimited
@racketparenfont{.}s is the same as putting the @racket[->] right @racketparenfont{.}s is the same as putting the @racket[->] right
after the enclosing opening parenthesis. See after the enclosing opening parenthesis. See
@ -1032,9 +1044,7 @@ after the enclosing opening parenthesis. See
information.} information.}
For example, For example,
@racketblock[(integer? boolean? . -> . integer?)] @racketblock[(integer? boolean? . -> . integer?)]
produces a contract on functions of two arguments. The first argument produces a contract on functions of two arguments. The first argument
must be an integer, and the second argument must be a boolean. The must be an integer, and the second argument must be a boolean. The
function must produce an integer. function must produce an integer.
@ -1043,12 +1053,16 @@ A domain specification may include a keyword. If so, the function must
accept corresponding (mandatory) keyword arguments, and the values for accept corresponding (mandatory) keyword arguments, and the values for
the keyword arguments must match the corresponding contracts. For the keyword arguments must match the corresponding contracts. For
example: example:
@racketblock[(integer? #:x boolean? . -> . integer?)] @racketblock[(integer? #:x boolean? . -> . integer?)]
is a contract on a function that accepts a by-position argument that is a contract on a function that accepts a by-position argument that
is an integer and a @racket[#:x] argument that is a boolean. is an integer and a @racket[#:x] argument that is a boolean.
As an example that uses an @racket[...], this contract:
@racketblock[(integer? string? ... integer? . -> . any)]
on a function insists that the first and last arguments to
the function must be integers (and there must be at least
two arguments) and any other arguments must be strings.
If @racket[any] is used as the last sub-form for @racket[->], no If @racket[any] is used as the last sub-form for @racket[->], no
contract checking is performed on the result of the function, and contract checking is performed on the result of the function, and
thus any number of values is legal (even different numbers on different thus any number of values is legal (even different numbers on different

View File

@ -501,8 +501,9 @@ but with syntax errors potentially phrased in terms of
The @racket[...] transformer binding prohibits @racket[...] from The @racket[...] transformer binding prohibits @racket[...] from
being used as an expression. This binding is useful only in syntax being used as an expression. This binding is useful only in syntax
patterns and templates, where it indicates repetitions of a pattern or patterns and templates (or other unrelated expression forms
template. See @racket[syntax-case] and @racket[syntax].} that treat it specially like @racket[->]), where it indicates repetitions
of a pattern or template. See @racket[syntax-case] and @racket[syntax].}
@defidform[_]{ @defidform[_]{

View File

@ -341,6 +341,34 @@
(eq? f (contract (-> any/c any) f 'pos 'neg))) (eq? f (contract (-> any/c any) f 'pos 'neg)))
#f) #f)
(test/spec-passed/result
'contract->...1
'((contract (-> integer? char? ... boolean? any)
(λ args args)
'pos 'neg)
1 #\a #\b #\c #f)
'(1 #\a #\b #\c #f))
(test/neg-blame
'contract->...2
'((contract (-> integer? char? ... boolean? any)
(λ args args)
'pos 'neg)
1 #\a "b" #\c #f))
(test/spec-passed/result
'contract->...3
'((contract (-> integer? ... any)
(λ args args)
'pos 'neg)
1 2 3 4 5 6 7)
'(1 2 3 4 5 6 7))
(test/neg-blame
'contract->...4
'((contract (-> integer? ... any)
(λ args args)
'pos 'neg)
1 2 3 4 5 6 7)
'(1 2 3 4 5 "6" 7))
(test/spec-passed (test/spec-passed
'contract-arrow-all-kwds2 'contract-arrow-all-kwds2
@ -557,4 +585,27 @@
'neg)) 'neg))
(void))) (void)))
(test/spec-passed/result
'->-order-of-evaluation1
'(let ([l '()])
(-> (begin (set! l (cons 1 l)) #f)
(begin (set! l (cons 2 l)) #f)
(begin (set! l (cons 3 l)) #f)
(begin (set! l (cons 4 l)) #f)
(begin (set! l (cons 5 l)) #f))
(reverse l))
'(1 2 3 4 5))
(test/spec-passed/result
'->-order-of-evaluation2
'(let ([l '()])
(-> (begin (set! l (cons 1 l)) #f)
(begin (set! l (cons 2 l)) #f)
(begin (set! l (cons 3 l)) #f)
...
(begin (set! l (cons 4 l)) #f)
(begin (set! l (cons 5 l)) #f)
(begin (set! l (cons 6 l)) #f))
(reverse l))
'(1 2 3 4 5))
) )

View File

@ -78,8 +78,16 @@
(valid-app-shapes '(2) '() '())) (valid-app-shapes '(2) '() '()))
(check-equal? (->-valid-app-shapes #'(-> integer? #:x any/c integer?)) (check-equal? (->-valid-app-shapes #'(-> integer? #:x any/c integer?))
(valid-app-shapes '(1) '(#:x) '())) (valid-app-shapes '(1) '(#:x) '()))
(check-equal? (->-valid-app-shapes #'(-> integer? #:x any/c #:y any/c integer?)) (check-equal? (->-valid-app-shapes #'(-> integer? (... ...) any))
(valid-app-shapes '(1) '(#:x #:y) '())) (valid-app-shapes 0 '() '()))
(check-equal? (->-valid-app-shapes #'(-> integer? integer? (... ...) any))
(valid-app-shapes 1 '() '()))
(check-equal? (->-valid-app-shapes #'(-> integer? integer? (... ...) integer? any))
(valid-app-shapes 2 '() '()))
(check-equal? (->-valid-app-shapes #'(-> integer? integer? (... ...) integer? boolean? char? any))
(valid-app-shapes 4 '() '()))
(check-equal? (->-valid-app-shapes #'(-> integer? boolean? char? (... ...) integer? char? any))
(valid-app-shapes 4 '() '()))
(check-equal? (->*-valid-app-shapes #'(->* (integer? #:x any/c #:y any/c) integer?)) (check-equal? (->*-valid-app-shapes #'(->* (integer? #:x any/c #:y any/c) integer?))
(valid-app-shapes '(1) '(#:x #:y) '())) (valid-app-shapes '(1) '(#:x #:y) '()))
@ -97,7 +105,6 @@
(check-equal? (->*-valid-app-shapes #'(->i ([m any/c]) ([o any/c]) #:rest [r any/c] [r any/c])) (check-equal? (->*-valid-app-shapes #'(->i ([m any/c]) ([o any/c]) #:rest [r any/c] [r any/c]))
(valid-app-shapes '(1 2 . 3) '() '())) (valid-app-shapes '(1 2 . 3) '() '()))
(check-true (valid-argument-list? #'(f x) (valid-app-shapes '(1 2 . 3) '() '()))) (check-true (valid-argument-list? #'(f x) (valid-app-shapes '(1 2 . 3) '() '())))
(check-true (valid-argument-list? #'(f x y) (valid-app-shapes '(1 2 . 3) '() '()))) (check-true (valid-argument-list? #'(f x y) (valid-app-shapes '(1 2 . 3) '() '())))
(check-true (valid-argument-list? #'(f x y a b c d) (valid-app-shapes '(1 2 . 3) '() '()))) (check-true (valid-argument-list? #'(f x y a b c d) (valid-app-shapes '(1 2 . 3) '() '())))

View File

@ -65,6 +65,8 @@
(test-name '(-> any/c boolean?) (-> any/c boolean?)) (test-name '(-> any/c boolean?) (-> any/c boolean?))
(test-name 'predicate/c predicate/c) (test-name 'predicate/c predicate/c)
(test-name '(-> integer? any/c ... boolean? any) (-> integer? any/c ... boolean? any))
(test-name '(->* (integer?) (string?) #:rest any/c (values char? any/c)) (test-name '(->* (integer?) (string?) #:rest any/c (values char? any/c))
(->* (integer?) (string?) #:rest any/c (values char? any/c))) (->* (integer?) (string?) #:rest any/c (values char? any/c)))
(test-name '(->* (integer? char?) (boolean?) any) (->* (integer? char?) (boolean?) any)) (test-name '(->* (integer? char?) (boolean?) any) (->* (integer? char?) (boolean?) any))

View File

@ -8,6 +8,7 @@
"misc.rkt" "misc.rkt"
"prop.rkt" "prop.rkt"
"guts.rkt" "guts.rkt"
"list.rkt"
(prefix-in arrow: "arrow.rkt") (prefix-in arrow: "arrow.rkt")
(only-in racket/unsafe/ops (only-in racket/unsafe/ops
unsafe-chaperone-procedure unsafe-chaperone-procedure
@ -461,10 +462,14 @@
(blame-add-context orig-blame (blame-add-context orig-blame
(format "the ~a argument of" (n->th n)) (format "the ~a argument of" (n->th n))
#:swap? #t)))) #:swap? #t))))
(define rest-blame
(if (ellipsis-rest-arg-ctc? rest)
(blame-swap orig-blame)
(blame-add-context orig-blame "the rest argument of"
#:swap? #t)))
(define partial-rest (and rest (define partial-rest (and rest
((get/build-late-neg-projection rest) ((get/build-late-neg-projection rest)
(blame-add-context orig-blame "the rest argument of" rest-blame)))
#:swap? #t))))
(define partial-ranges (define partial-ranges
(if rngs (if rngs
(for/list ([rng (in-list rngs)]) (for/list ([rng (in-list rngs)])

View File

@ -9,6 +9,7 @@
"guts.rkt" "guts.rkt"
"generate.rkt" "generate.rkt"
"arrow-higher-order.rkt" "arrow-higher-order.rkt"
"list.rkt"
racket/stxparam racket/stxparam
(prefix-in arrow: "arrow.rkt")) (prefix-in arrow: "arrow.rkt"))
@ -551,7 +552,8 @@
[regular-args '()] [regular-args '()]
[kwds '()] [kwds '()]
[kwd-args '()] [kwd-args '()]
[let-bindings '()]) [let-bindings '()]
[ellipsis #f])
(cond (cond
[(null? args) [(null? args)
(define sorted (define sorted
@ -561,18 +563,52 @@
(values (reverse regular-args) (values (reverse regular-args)
(map car sorted) (map car sorted)
(map cdr sorted) (map cdr sorted)
let-bindings)] (reverse let-bindings)
#f)]
[else [else
(with-syntax ([(arg-x) (generate-temporaries (list (car args)))]) (cond
(syntax-case (car args) () [(and (identifier? (car args)) (free-identifier=? (car args) #'(... ...)))
[kwd (when ellipsis
(keyword? (syntax-e #'kwd)) (raise-syntax-error '-> "expected at most one ellipsis"
(begin stx (car args) ellipsis))
(when (null? regular-args)
(raise-syntax-error '->
"expected the ellipsis to follow a contract"
stx
(car args)))
(for ([arg (in-list (cdr args))])
(when (keyword? (syntax-e arg))
(raise-syntax-error '->
"keywords are not allowed after the ellipsis"
stx arg)))
(define sorted
(sort (map cons kwds kwd-args)
keyword<?
#:key (compose syntax-e car)))
(define arg-xes (generate-temporaries (cdr args)))
(values (reverse (cdr regular-args))
(map car sorted)
(map cdr sorted)
(append (reverse let-bindings)
(for/list ([arg-exp (cdr args)]
[arg-x (in-list arg-xes)])
#`[#,arg-x #,(syntax-property arg-exp
'racket/contract:negative-position
this->)]))
(cons (car regular-args) arg-xes))]
[(keyword? (syntax-e (car args)))
(when (null? (cdr args)) (when (null? (cdr args))
(raise-syntax-error '-> (raise-syntax-error '->
"expected a contract to follow the keyword (plus the range)" "expected a contract to follow the keyword (plus the range)"
stx stx
(car args))) (car args)))
(when (and (identifier? (cadr args))
(free-identifier=? (cadr args) #'(... ...)))
(raise-syntax-error '->
"expected a contract to follow a keyword, not an ellipsis"
stx
(car args)))
(with-syntax ([(arg-x) (generate-temporaries (list (car args)))])
(loop (cddr args) (loop (cddr args)
regular-args regular-args
(cons (car args) kwds) (cons (car args) kwds)
@ -580,8 +616,10 @@
(cons #`[arg-x #,(syntax-property (cadr args) (cons #`[arg-x #,(syntax-property (cadr args)
'racket/contract:negative-position 'racket/contract:negative-position
this->)] this->)]
let-bindings)))] let-bindings)
ellipsis))]
[else [else
(with-syntax ([(arg-x) (generate-temporaries (list (car args)))])
(loop (cdr args) (loop (cdr args)
(cons #'arg-x regular-args) (cons #'arg-x regular-args)
kwds kwds
@ -589,16 +627,20 @@
(cons #`[arg-x #,(syntax-property (car args) (cons #`[arg-x #,(syntax-property (car args)
'racket/contract:negative-position 'racket/contract:negative-position
this->)] this->)]
let-bindings))]))]))) let-bindings)
ellipsis))])])))
(define-for-syntax (->-valid-app-shapes stx) (define-for-syntax (->-valid-app-shapes stx)
(syntax-case stx () (syntax-case stx ()
[(_ args ...) [(_ args ...)
(let () (let ()
(define this-> (gensym 'this->)) (define this-> (gensym 'this->))
(define-values (regular-args kwds kwd-args let-bindings) (define-values (regular-args kwds kwd-args let-bindings ellipsis-info)
(parse-arrow-args stx (syntax->list #'(args ...)) this->)) (parse-arrow-args stx (syntax->list #'(args ...)) this->))
(valid-app-shapes (list (- (length regular-args) 1)) (define arg-count (- (length regular-args) 1))
(valid-app-shapes (if ellipsis-info
(+ arg-count (- (length ellipsis-info) 1))
(list arg-count))
(map syntax->datum kwds) (map syntax->datum kwds)
'()))])) '()))]))
@ -610,7 +652,7 @@
[(_ args ... rng) [(_ args ... rng)
(let () (let ()
(define this-> (gensym 'this->)) (define this-> (gensym 'this->))
(define-values (regular-args kwds kwd-args let-bindings) (define-values (regular-args kwds kwd-args let-bindings ellipsis-info)
(parse-arrow-args stx (syntax->list #'(args ...)) this->)) (parse-arrow-args stx (syntax->list #'(args ...)) this->))
(define (add-pos-obligations stxes) (define (add-pos-obligations stxes)
(for/list ([stx (in-list stxes)]) (for/list ([stx (in-list stxes)])
@ -622,7 +664,7 @@
[rng (add-pos-obligations (list #'rng))])) [rng (add-pos-obligations (list #'rng))]))
(define-values (plus-one-arity-function chaperone-constructor) (define-values (plus-one-arity-function chaperone-constructor)
(build-plus-one-arity-function+chaperone-constructor (build-plus-one-arity-function+chaperone-constructor
regular-args '() kwds '() #f #f #f rngs #f #f)) regular-args '() kwds '() #f #f (and ellipsis-info #t) rngs #f #f))
(syntax-property (syntax-property
#`(let #,let-bindings #`(let #,let-bindings
#,(quasisyntax/loc stx #,(quasisyntax/loc stx
@ -634,7 +676,10 @@
#`(list #,@rngs) #`(list #,@rngs)
#'#f) #'#f)
#,plus-one-arity-function #,plus-one-arity-function
#,chaperone-constructor))) #,chaperone-constructor
#,(if ellipsis-info
#`(ellipsis-rest-arg #,(length regular-args) #,@ellipsis-info)
#'#f))))
'racket/contract:contract 'racket/contract:contract
(vector this-> (vector this->
;; the -> in the original input to this guy ;; the -> in the original input to this guy
@ -805,12 +850,13 @@
mandatory-kwds mandatory-raw-kwd-doms mandatory-kwds mandatory-raw-kwd-doms
raw-rngs raw-rngs
plus-one-arity-function plus-one-arity-function
chaperone-constructor) chaperone-constructor
raw-rest-ctc)
(build--> '-> (build--> '->
raw-regular-doms '() raw-regular-doms '()
mandatory-kwds mandatory-raw-kwd-doms mandatory-kwds mandatory-raw-kwd-doms
'() '() '() '()
#f raw-rest-ctc
#f raw-rngs #f #f raw-rngs #f
plus-one-arity-function plus-one-arity-function
chaperone-constructor)) chaperone-constructor))
@ -1168,16 +1214,29 @@
[(and (andmap kwd-info-mandatory? (base->-kwd-infos ctc)) [(and (andmap kwd-info-mandatory? (base->-kwd-infos ctc))
(= (base->-min-arity ctc) (= (base->-min-arity ctc)
(length (base->-doms ctc))) (length (base->-doms ctc)))
(not (base->-rest ctc)) (or (not (base->-rest ctc))
(ellipsis-rest-arg-ctc? (base->-rest ctc)))
(not (base->-pre? ctc)) (not (base->-pre? ctc))
(not (base->-post? ctc))) (not (base->-post? ctc)))
`(-> ,@(map contract-name (base->-doms ctc)) (define kwd-args
,@(apply (apply
append append
(for/list ([kwd-info (base->-kwd-infos ctc)]) (for/list ([kwd-info (in-list (base->-kwd-infos ctc))])
(list (kwd-info-kwd kwd-info) (list (kwd-info-kwd kwd-info)
(contract-name (kwd-info-ctc kwd-info))))) (contract-name (kwd-info-ctc kwd-info))))))
(cond
[(ellipsis-rest-arg-ctc? (base->-rest ctc))
`(-> ,@(map contract-name (base->-doms ctc))
,@kwd-args
,(contract-name (*list-ctc-prefix (base->-rest ctc)))
...
,@(for/list ([ctc (in-list (*list-ctc-suffix (base->-rest ctc)))])
(contract-name ctc))
,rng-sexp)] ,rng-sexp)]
[else
`(-> ,@(map contract-name (base->-doms ctc))
,@kwd-args
,rng-sexp)])]
[else [else
(define (take l n) (reverse (list-tail (reverse l) (- (length l) n)))) (define (take l n) (reverse (list-tail (reverse l) (- (length l) n))))
(define mandatory-args (define mandatory-args

View File

@ -4,6 +4,7 @@
"blame.rkt" "blame.rkt"
"prop.rkt" "prop.rkt"
"misc.rkt" "misc.rkt"
"list.rkt"
racket/stxparam racket/stxparam
racket/private/performance-hint) racket/private/performance-hint)
(require (for-syntax racket/base) (require (for-syntax racket/base)
@ -619,7 +620,8 @@
[func (base->-func ctc)] [func (base->-func ctc)]
[dom-length (length (base->-doms/c ctc))] [dom-length (length (base->-doms/c ctc))]
[optionals-length (length (base->-optional-doms/c ctc))] [optionals-length (length (base->-optional-doms/c ctc))]
[has-rest? (and (base->-dom-rest/c ctc) #t)] [rest-ctc (base->-dom-rest/c ctc)]
[has-rest? (and rest-ctc #t)]
[pre (base->-pre ctc)] [pre (base->-pre ctc)]
[post (base->-post ctc)] [post (base->-post ctc)]
[mtd? (base->-mtd? ctc)]) [mtd? (base->-mtd? ctc)])
@ -628,13 +630,21 @@
(define partial-doms (define partial-doms
(for/list ([dom (in-list doms-proj)] (for/list ([dom (in-list doms-proj)]
[n (in-naturals 1)]) [n (in-naturals 1)])
(dom (blame-add-context orig-blame (define dom-blame
(cond
[(and has-rest?
(n . > . dom-length)
(ellipsis-rest-arg-ctc? rest-ctc))
(blame-swap orig-blame)]
[else
(blame-add-context orig-blame
(if (and has-rest? (if (and has-rest?
(n . > . dom-length)) (n . > . dom-length))
"the rest argument of" "the rest argument of"
(format "the ~a argument of" (format "the ~a argument of"
(n->th n))) (n->th n)))
#:swap? #t)))) #:swap? #t)]))
(dom dom-blame)))
(define partial-optional-doms (define partial-optional-doms
(for/list ([dom (in-list doms-optional-proj)] (for/list ([dom (in-list doms-optional-proj)]
[n (in-naturals (+ 1 (length doms-proj)))]) [n (in-naturals (+ 1 (length doms-proj)))])

View File

@ -15,7 +15,9 @@
blame-add-car-context blame-add-car-context
blame-add-cdr-context blame-add-cdr-context
raise-not-cons-blame-error raise-not-cons-blame-error
*list/c) *list/c ellipsis-rest-arg
(struct-out ellipsis-rest-arg-ctc)
(struct-out *list-ctc))
(define (listof-generate ctc) (define (listof-generate ctc)
(cond (cond
@ -873,20 +875,26 @@
(contract-struct-stronger? suf that-elem)))] (contract-struct-stronger? suf that-elem)))]
[else #f])) [else #f]))
(define (*list/c-late-neg-projection ctc flat?) (define (*list/c-late-neg-projection ctc start-index flat?)
(define prefix-lnp (contract-late-neg-projection (*list-ctc-prefix ctc))) (define prefix-lnp (contract-late-neg-projection (*list-ctc-prefix ctc)))
(define suffix-lnps (map contract-late-neg-projection (*list-ctc-suffix ctc))) (define suffix-lnps (map contract-late-neg-projection (*list-ctc-suffix ctc)))
(define suffix?s-len (length suffix-lnps)) (define suffix?s-len (length suffix-lnps))
(λ (blame) (λ (blame)
(define prefix-val-acceptor (prefix-lnp (blame-add-context blame "the prefix of"))) (define prefix-val-acceptor
(prefix-lnp (if start-index
(blame-add-context blame "the repeated argument of")
(blame-add-context blame "the prefix of"))))
(define suffix-val-acceptors (define suffix-val-acceptors
(for/list ([i (in-naturals)] (for/list ([i (in-naturals)]
[suffix-lnp (in-list suffix-lnps)]) [suffix-lnp (in-list suffix-lnps)])
(define which (- suffix?s-len i)) (define which (- suffix?s-len i))
(define msg (define msg
(if (= 1 which) (if (= 1 which)
"the last element of" (if start-index "the last argument of" "the last element of")
(format "the ~a to the last element of" (n->th which)))) (format (if start-index
"the ~a to the last argument of"
"the ~a to the last element of")
(n->th which))))
(suffix-lnp (blame-add-context blame msg)))) (suffix-lnp (blame-add-context blame msg))))
(λ (val neg-party) (λ (val neg-party)
(cond (cond
@ -914,12 +922,18 @@
(loop (cdr remainder-to-process) (cdr end)) (loop (cdr remainder-to-process) (cdr end))
(cons fst (loop (cdr remainder-to-process) (cdr end))))]))] (cons fst (loop (cdr remainder-to-process) (cdr end))))]))]
[else [else
(if start-index
(raise-blame-error
blame
val
'(expected: "at least ~a arguments"
(+ start-index suffix?s-len)))
(raise-blame-error (raise-blame-error
blame blame
val val
'(expected: "list? with at least ~a elements" given: "~e") '(expected: "list? with at least ~a elements" given: "~e")
suffix?s-len suffix?s-len
val)])] val))])]
[else (raise-blame-error [else (raise-blame-error
blame blame
val val
@ -938,7 +952,7 @@
#:generate *list/c-generate #:generate *list/c-generate
#:exercise *list/c-exercise #:exercise *list/c-exercise
#:stronger *list/c-stronger #:stronger *list/c-stronger
#:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #t)) #:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f #t))
#:list-contract? (λ (c) #t))) #:list-contract? (λ (c) #t)))
(struct chaperone-*list/c *list-ctc () (struct chaperone-*list/c *list-ctc ()
#:property prop:contract #:property prop:contract
@ -948,7 +962,7 @@
#:generate *list/c-generate #:generate *list/c-generate
#:exercise *list/c-exercise #:exercise *list/c-exercise
#:stronger *list/c-stronger #:stronger *list/c-stronger
#:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f)) #:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f #f))
#:list-contract? (λ (c) #t))) #:list-contract? (λ (c) #t)))
(struct impersonator-*list/c *list-ctc () (struct impersonator-*list/c *list-ctc ()
#:property prop:contract #:property prop:contract
@ -958,7 +972,7 @@
#:generate *list/c-generate #:generate *list/c-generate
#:exercise *list/c-exercise #:exercise *list/c-exercise
#:stronger *list/c-stronger #:stronger *list/c-stronger
#:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f)) #:late-neg-projection (λ (ctc) (*list/c-late-neg-projection ctc #f #f))
#:list-contract? (λ (c) #t))) #:list-contract? (λ (c) #t)))
(define (*list/c ele . rest) (define (*list/c ele . rest)
@ -978,3 +992,51 @@
(listof any/c) (listof any/c)
(cons/c any/c any/c) (cons/c any/c any/c)
(list/c)) (list/c))
;; used by -> when it gets an ellipsis. This
;; contract turns into the equivalent of the #:rest
;; argument (if the same contract had been an ->*)
(define (ellipsis-rest-arg start-index . eles)
(define ctcs (coerce-contracts '-> eles))
(cond
[(andmap flat-contract? ctcs)
(flat-ellipsis-rest-arg (car ctcs) (cdr ctcs) start-index)]
[(andmap chaperone-contract? ctcs)
(chaperone-ellipsis-rest-arg (car ctcs) (cdr ctcs) start-index)]
[else
(impersonator-ellipsis-rest-arg (car ctcs) (cdr ctcs) start-index)]))
(struct ellipsis-rest-arg-ctc *list-ctc (start-index))
(struct flat-ellipsis-rest-arg ellipsis-rest-arg-ctc ()
#:property prop:contract
(build-contract-property
#:name (λ (ctc) (error 'flat-ellipsis-rest-arg "the name property shouldn't be called!"))
#:first-order *list/c-first-order
#:generate *list/c-generate
#:exercise *list/c-exercise
#:stronger *list/c-stronger
#:late-neg-projection
(λ (ctc) (*list/c-late-neg-projection ctc (ellipsis-rest-arg-ctc-start-index ctc) #t))
#:list-contract? (λ (c) #t)))
(struct chaperone-ellipsis-rest-arg ellipsis-rest-arg-ctc ()
#:property prop:contract
(build-contract-property
#:name (λ (ctc) (error 'flat-ellipsis-rest-arg "the name property shouldn't be called!"))
#:first-order *list/c-first-order
#:generate *list/c-generate
#:exercise *list/c-exercise
#:stronger *list/c-stronger
#:late-neg-projection
(λ (ctc) (*list/c-late-neg-projection ctc (ellipsis-rest-arg-ctc-start-index ctc) #f))
#:list-contract? (λ (c) #t)))
(struct impersonator-ellipsis-rest-arg ellipsis-rest-arg-ctc ()
#:property prop:contract
(build-contract-property
#:name (λ (ctc) (error 'flat-ellipsis-rest-arg "the name property shouldn't be called!"))
#:first-order *list/c-first-order
#:generate *list/c-generate
#:exercise *list/c-exercise
#:stronger *list/c-stronger
#:late-neg-projection
(λ (ctc) (*list/c-late-neg-projection ctc (ellipsis-rest-arg-ctc-start-index ctc) #f))
#:list-contract? (λ (c) #t)))