arity+keywords.rkt (23136B)
1 #lang racket/base 2 3 (provide (struct-out arity+keywords) 4 empty-arity+keywords 5 any-arity+keywords 6 procedure-arity+keywords 7 procedure-reduce-arity+keywords 8 procedure-reduce-keyword-arity/sort 9 arity+keywords-matches? 10 procedure-arity+keywords-matches? 11 procedure-arity+keywords-matches?/c 12 procedure-required-keywords 13 procedure-allowed-keywords 14 arity+keywords-combine/or arity-combine/or kws-combine/or 15 arity+keywords-combine/and arity-combine/and kws-combine/and 16 arity+keywords-combine 17 arity+keywords-add arity-add 18 arity+keywords-subtract arity-subtract 19 arity-map 20 kws-sort 21 ) 22 23 (require racket/function 24 racket/bool 25 racket/contract/base 26 racket/list 27 racket/match 28 (for-syntax racket/base 29 )) 30 31 (module+ test 32 (require rackunit racket/local racket/math)) 33 34 (define (arity+keywords-guard arity required-kws allowed-kws _) 35 (unless (procedure-arity? arity) 36 (error 'arity+keywords "expected procedure-arity? for first argument, given ~v" arity)) 37 (unless ((listof keyword?) required-kws) 38 (error 'arity+keywords 39 "expcetd (listof keyword?) for second argument, given ~v" 40 required-kws)) 41 (unless ((or/c (listof keyword?) #t #f) allowed-kws) 42 (error 'arity+keywords 43 "expcetd (or/c (listof keyword?) #f) for third argument, given ~v" 44 required-kws)) 45 (define new-arity (normalize-arity arity)) 46 (define new-required-kws 47 (cond [(empty? new-arity) '()] 48 [else (kws-sort required-kws)])) 49 (define new-allowed-kws 50 (cond [(empty? new-arity) '()] 51 [(list? allowed-kws) 52 (kws-combine/or new-required-kws allowed-kws)] 53 [else #f])) 54 (values new-arity new-required-kws new-allowed-kws)) 55 56 ;; arity+keywords : Procedure-Arity (Listof Keyword) (or/c (Listof Keyword) #f) -> Arity+Keywords 57 (struct arity+keywords (arity required-kws allowed-kws) #:transparent 58 #:guard arity+keywords-guard) 59 60 61 ;; procedure-arity+keywords : Procedure -> Arity+Keywords 62 (define (procedure-arity+keywords proc) 63 (define arity (procedure-arity proc)) 64 (define-values (req-kws allowed-kws) 65 (procedure-keywords proc)) 66 (arity+keywords arity req-kws allowed-kws)) 67 68 (define (procedure-required-keywords proc) 69 (arity+keywords-required-kws (procedure-arity+keywords proc))) 70 71 (define (procedure-allowed-keywords proc) 72 (arity+keywords-allowed-kws (procedure-arity+keywords proc))) 73 74 ;; proceudre-reduce-arity+keywords : Procedure Arity+Keywords -> Procedure 75 (define (procedure-reduce-arity+keywords proc a) 76 (match-define (arity+keywords arity required-kws allowed-kws) a) 77 (procedure-reduce-keyword-arity 78 proc 79 arity 80 required-kws 81 allowed-kws)) 82 83 ;; like procedure-reduce-keyword-arity, but without the constraint that the kws must be sorted 84 (define (procedure-reduce-keyword-arity/sort proc arity required-kws allowed-kws) 85 (procedure-reduce-arity+keywords 86 proc 87 (arity+keywords arity required-kws allowed-kws))) 88 89 ;; arity+keywords-matches? : Arity+Keywords Natural (Listof Keyword) -> Boolean 90 ;; see also arity+keywords-includes? 91 (define (arity+keywords-matches? arity+kws n kws) 92 (match-define (arity+keywords arity required-kws allowed-kws) arity+kws) 93 (and (arity-includes? arity n) 94 (or (false? allowed-kws) 95 (for/and ([kw (in-list kws)]) 96 (member kw allowed-kws))) 97 (for/and ([required-kw (in-list required-kws)]) 98 (member required-kw kws)) 99 #t)) 100 101 ;; procedure-arity+keywords-matches? : Procedure Natural (Listof Keyword) -> Boolean 102 (define (procedure-arity+keywords-matches? proc n kws) 103 (arity+keywords-matches? (procedure-arity+keywords proc) n kws)) 104 105 ;; procedure-arity+keywords-matches?/c : Natural (Listof Keyword) -> (Procedure -> Boolean) 106 (define (procedure-arity+keywords-matches?/c n kws) 107 (flat-named-contract 108 `(procedure-arity+keywords-matches?/c ,n (quote ,kws)) 109 (lambda (proc) 110 (procedure-arity+keywords-matches? proc n kws)))) 111 112 ;; arity+keywords-includes? : Arity+Keywords Arity+Keywords -> Boolean 113 ;; see also arity+keywords-matches? 114 (define (arity+keywords-includes? a1 a2) 115 (match-define (arity+keywords a1.arity a1.req-kws a1.allowed-kws) a1) 116 (match-define (arity+keywords a2.arity a2.req-kws a2.allowed-kws) a2) 117 (and (arity-includes? a1.arity a2.arity) 118 (for/and ([a1-kw (in-list a1.req-kws)]) 119 (member a1-kw a2.req-kws)) 120 (cond [(false? a1.allowed-kws) #t] 121 [(false? a2.allowed-kws) #f] 122 [else (for/and ([a2-kw (in-list a2.allowed-kws)]) 123 (member a2-kw a1.allowed-kws))]) 124 #t)) 125 126 ;; arity+keywords-combine/or : Arity+Keywords ... -> Arity+Keywords 127 (define arity+keywords-combine/or 128 (case-lambda 129 [() empty-arity+keywords] 130 [(a) a] 131 [(a1 a2) (match-define (arity+keywords a1.arity a1.required-kws a1.allowed-kws) a1) 132 (match-define (arity+keywords a2.arity a2.required-kws a2.allowed-kws) a2) 133 (cond 134 [(andmap empty? (list a1.arity a2.arity)) empty-arity+keywords] 135 [(empty? a1.arity) a2] 136 [(empty? a2.arity) a1] 137 [else 138 (define arity 139 (arity-combine/or a1.arity a2.arity)) 140 (define required-kws 141 (kws-combine/and a1.required-kws a2.required-kws)) 142 (define allowed-kws 143 (kws-combine/or a1.allowed-kws a2.allowed-kws)) 144 (arity+keywords arity required-kws allowed-kws)])] 145 [(a1 . rest-args) (arity+keywords-combine/or a1 (apply arity+keywords-combine/or rest-args))] 146 )) 147 148 (define (arity+keywords-combine-warning) 149 (with-handlers ([exn:fail? (λ (e) ((error-display-handler) (exn-message e) e))]) 150 (error 'arity+keywords-combine 151 (string-append "please use arity+keywords-combine/or instead" "\n" 152 " (to avoid confusion with arity+keywords-combine/and)")))) 153 (define-syntax arity+keywords-combine 154 (lambda (stx) 155 (with-handlers ([exn:fail:syntax? (λ (e) ((error-display-handler) (exn-message e) e))]) 156 (raise-syntax-error #f 157 (string-append "please use arity+keywords-combine/or instead" "\n" 158 " (to avoid confusion with arity+keywords-combine/and)") 159 stx)) 160 (syntax-case stx () 161 [(arity+keywords-combine . stuff) 162 (quasisyntax/loc stx 163 (begin 164 #,(syntax/loc stx (arity+keywords-combine-warning)) 165 #,(syntax/loc stx (arity+keywords-combine/or . stuff))))] 166 [arity+keywords-combine 167 (quasisyntax/loc stx 168 (begin 169 #,(syntax/loc stx (arity+keywords-combine-warning)) 170 #,(quasisyntax/loc stx 171 (λ args #,(syntax/loc stx (arity+keywords-combine-warning)) 172 (apply arity+keywords-combine/or args)))))]))) 173 174 ;; arity+keywords-combine/and : Arity+Keywords ... -> Arity+Keywords 175 (define arity+keywords-combine/and 176 (case-lambda 177 [() any-arity+keywords] 178 [(a) a] 179 [(a1 a2) (match-define (arity+keywords a1.arity a1.required-kws a1.allowed-kws) a1) 180 (match-define (arity+keywords a2.arity a2.required-kws a2.allowed-kws) a2) 181 (define arity 182 (arity-combine/and a1.arity a2.arity)) 183 (define required-kws 184 (kws-combine/or a1.required-kws a2.required-kws)) 185 (define allowed-kws 186 (kws-combine/and a1.allowed-kws a2.allowed-kws)) 187 (cond [(for/and ([req-kw (in-list required-kws)]) 188 (member req-kw allowed-kws)) 189 (arity+keywords arity required-kws allowed-kws)] 190 [else empty-arity+keywords])] 191 [(a1 . rest-args) (arity+keywords-combine/and a1 (apply arity+keywords-combine/and rest-args))] 192 )) 193 194 ;; arity-combine/or : Procedure-Arity ... -> Procedure-Arity 195 (define (arity-combine/or . args) 196 (normalize-arity (flatten args))) 197 198 ;; arity-combine/and : Procedure-Arity Procedure-Arity -> Procedure-Arity 199 (define (arity-combine/and a1 a2) 200 (let ([a1 (normalize-arity a1)] 201 [a2 (normalize-arity a2)]) 202 (cond [(arity-includes? a1 a2) a2] 203 [(arity-includes? a2 a1) a1] 204 [(number? a1) 205 (cond [(arity-includes? a2 a1) a1] 206 [else '()])] 207 [(number? a2) 208 (cond [(arity-includes? a1 a2) a2] 209 [else '()])] 210 [(arity-at-least? a1) 211 (cond [(arity-includes? a2 a1) a1] 212 [(number? a2) '()] 213 [(arity-at-least? a2) 214 (arity-at-least (max (arity-at-least-value a1) 215 (arity-at-least-value a2)))] 216 [(list? a2) 217 (normalize-arity 218 (flatten 219 (for/list ([n (in-list a2)]) 220 (arity-combine/and a1 n))))] 221 [else (error 'arity-combine/and "this should never happen")])] 222 [(arity-at-least? a2) 223 (cond [(arity-includes? a1 a2) a2] 224 [(number? a1) '()] 225 [(arity-at-least? a1) 226 (arity-at-least (max (arity-at-least-value a1) 227 (arity-at-least-value a2)))] 228 [(list? a1) 229 (normalize-arity 230 (flatten 231 (for/list ([n (in-list a1)]) 232 (arity-combine/and a2 n))))] 233 [else (error 'arity-combine/and "this should never happen")])] 234 [(list? a1) 235 (normalize-arity 236 (flatten 237 (for/list ([n (in-list a1)]) 238 (arity-combine/and a2 n))))] 239 [(list? a2) 240 (normalize-arity 241 (flatten 242 (for/list ([n (in-list a2)]) 243 (arity-combine/and a1 n))))] 244 [else (error 'arity-combine/and "this should never happen")]))) 245 246 ;; kws-combine/or 247 ;; note that this combines the allowed keywords in an or-like way. 248 ;; for the required keywords, arity+keywords-combine/or actually uses kws-append/and 249 (define (kws-combine/or . args) 250 (cond [(empty? args) '()] 251 [(ormap false? args) #f] 252 [else 253 (kws-sort (apply append args))])) 254 255 (define kws-combine/and 256 (case-lambda 257 [() #f] 258 [(a) (kws-sort a)] 259 [(a b) 260 (kws-sort 261 (cond [(false? a) b] 262 [(false? b) a] 263 [else 264 (for*/list ([a-kw (in-list a)] 265 [b-kw (in-list b)] 266 #:when (equal? a-kw b-kw)) 267 a-kw)]))] 268 [(a b . rst) 269 (apply kws-combine/and (kws-combine/and a b) rst)])) 270 271 (define (kws-sort kws) 272 (cond [(false? kws) #f] 273 [else (sort (remove-duplicates kws) keyword<?)])) 274 275 276 277 ;; arity+keywords-add : Arity+Keywords Natural (Listof Keyword) (Listof Keyword) -> Arity+Keywords 278 (define (arity+keywords-add a n req-kws allwd-kws) 279 (match-define (arity+keywords a.arity a.req-kws a.allowed-kws) a) 280 (define arity (arity-add a.arity n)) 281 (define required-kws (kws-combine/or a.req-kws req-kws)) 282 (define allowed-kws (kws-combine/or a.allowed-kws req-kws allwd-kws)) 283 (arity+keywords arity req-kws allowed-kws)) 284 285 ;; arity+keywords-subtract : Arity+Keywords Natural (Listof Keyword) -> Arity+Keywords 286 (define (arity+keywords-subtract a n kws) 287 (match-define (arity+keywords a.arity a.req-kws a.allowed-kws) a) 288 (define arity (arity-subtract a.arity n)) 289 (cond [(empty? arity) empty-arity+keywords] 290 [(not (list? a.allowed-kws)) 291 (define req-kws 292 (remove* kws a.req-kws)) 293 (define allowed-kws #f) 294 (arity+keywords arity req-kws allowed-kws)] 295 [(not (for/and ([kw (in-list kws)]) 296 (member kw a.allowed-kws))) 297 empty-arity+keywords] 298 [else 299 (define req-kws 300 (remove* kws a.req-kws)) 301 (define allowed-kws 302 (remove* kws a.allowed-kws)) 303 (arity+keywords arity req-kws allowed-kws)])) 304 305 ;; arity-add : Procedure-Arity Integer -> Procedure-Arity 306 ;; n could be negative 307 (define (arity-add arity n) 308 (arity-map 309 (λ (a) 310 (arity-add/simple a n)) 311 arity)) 312 313 ;; arity-subtract : Procedure-Arity Natural -> Procedure-Arity 314 (define (arity-subtract arity n) 315 (arity-add arity (- n))) 316 317 ;; arity-add/simple : [(U Natural (arity-at-least Natural)) Integer -> Procedure-Arity] 318 ;; n could be negative 319 (define (arity-add/simple a n) 320 (cond [(number? a) 321 (define new-a (+ a n)) 322 (cond [(negative? new-a) '()] 323 [else new-a])] 324 [(arity-at-least? a) 325 (define a.n (arity-at-least-value a)) 326 (define new-a.n (+ a.n n)) 327 (cond [(negative? new-a.n) (arity-at-least 0)] 328 [else (arity-at-least new-a.n)])] 329 [else (error 'arity-add/simple "this should never happen")])) 330 331 ;;(define-type Nat/Aal->Ar [(U Natural (arity-at-least Natural)) -> Procedure-Arity]) 332 333 ;; arity-map : [Nat/Aal->Ar Procedure-Arity -> Procedure-Arity] 334 (define (arity-map proc arity) 335 (let ([arity (normalize-arity arity)]) 336 (cond [(number? arity) (normalize-arity (proc arity))] 337 [(arity-at-least? arity) (normalize-arity (proc arity))] 338 [(list? arity) (normalize-arity (flatten (map proc arity)))] 339 [else (error 'arity-map "this should never happen, given ~v" arity)]))) 340 341 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 342 343 (define empty-arity+keywords (arity+keywords '() '() '())) 344 (define any-arity+keywords (arity+keywords (arity-at-least 0) '() #f)) 345 346 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 347 348 (module+ test 349 (test-case "the arity+keywords constructor and guard" 350 (check-equal? (arity+keywords (list 1 (arity-at-least 3) 2) '() #t) 351 (arity+keywords (arity-at-least 1) '() #f)) 352 (check-equal? (arity+keywords '(1) '(#:a #:b) '(#:c)) 353 (arity+keywords 1 '(#:a #:b) '(#:a #:b #:c)))) 354 355 (test-case "procedure-arity+keywords and procedure-reduce-arity+keywords" 356 (define proc (make-keyword-procedure void)) 357 (check-equal? (procedure-arity+keywords proc) 358 (arity+keywords (arity-at-least 0) '() #f)) 359 (check-equal? (procedure-arity+keywords (procedure-reduce-arity proc 5)) 360 (arity+keywords 5 '() '())) 361 (define proc-with-arity 362 (procedure-reduce-arity+keywords 363 proc 364 (arity+keywords 3 '(#:kw #:other-kw) '(#:kw #:other-kw #:optional-kw)))) 365 (check-equal? (procedure-arity+keywords proc-with-arity) 366 (arity+keywords 3 '(#:kw #:other-kw) '(#:kw #:other-kw #:optional-kw))) 367 (check-equal? (procedure-arity proc-with-arity) 3) 368 (check-equal? (call-with-values (λ () (procedure-keywords proc-with-arity)) list) 369 (list '(#:kw #:other-kw) '(#:kw #:optional-kw #:other-kw)))) 370 371 (test-case "arity+keywords-matches?" 372 (check-true (arity+keywords-matches? (arity+keywords 0 '() '()) 0 '())) 373 (check-false (arity+keywords-matches? (arity+keywords 0 '() '()) 1 '())) 374 (check-false (arity+keywords-matches? (arity+keywords '() '() #f) 0 '())) 375 (check-true (arity+keywords-matches? (arity+keywords (list 2 (arity-at-least 5)) 376 '() '()) 377 2 '())) 378 (check-false (arity+keywords-matches? (arity+keywords (list 2 (arity-at-least 5)) 379 '() '()) 380 3 '())) 381 (check-true (arity+keywords-matches? (arity+keywords (list 2 (arity-at-least 5)) 382 '() '()) 383 5 '())) 384 (check-true (arity+keywords-matches? (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c)) 385 0 '(#:a #:b))) 386 (check-true (arity+keywords-matches? (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c)) 387 0 '(#:a #:b #:c))) 388 (check-false (arity+keywords-matches? (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c)) 389 0 '(#:a #:c))) 390 (check-true (arity+keywords-matches? (arity+keywords 0 '() #f) 391 0 '(#:whatever)))) 392 393 (test-case "arity+keywords-includes?" 394 (check-true (arity+keywords-includes? (arity+keywords 1 '() '()) 395 (arity+keywords 1 '() '()))) 396 (check-true (arity+keywords-includes? (arity+keywords '(1 2) '() '()) 397 (arity+keywords 1 '() '()))) 398 (check-false (arity+keywords-includes? (arity+keywords 1 '() '()) 399 (arity+keywords '(1 2) '() '()))) 400 (check-true (arity+keywords-includes? (arity+keywords 0 '() #f) 401 (arity+keywords 0 '() '(#:a)))) 402 (check-true (arity+keywords-includes? (arity+keywords 0 '() #f) 403 (arity+keywords 0 '(#:a) '(#:a)))) 404 (check-false (arity+keywords-includes? (arity+keywords 0 '(#:a) #f) 405 (arity+keywords 0 '() '(#:a)))) 406 (check-true (arity+keywords-includes? (arity+keywords 0 '() '(#:a #:b)) 407 (arity+keywords 0 '(#:a) '(#:a)))) 408 (check-false (arity+keywords-includes? (arity+keywords 0 '() '()) 409 (arity+keywords 0 '() #f)))) 410 411 (test-case "arity+keywords-combine/or" 412 (check-equal? (arity+keywords-combine/or) (arity+keywords '() '() '())) 413 (check-equal? (arity+keywords-combine/or (arity+keywords '(4 9 16) '(#:a #:b) '(#:a #:b #:c))) 414 (arity+keywords '(4 9 16) '(#:a #:b) '(#:a #:b #:c))) 415 (check-equal? (arity+keywords-combine/or (arity+keywords 1 '(#:a) '(#:a #:b #:c)) 416 (arity+keywords 2 '(#:a #:b) '(#:a #:b #:d))) 417 (arity+keywords '(1 2) '(#:a) '(#:a #:b #:c #:d)))) 418 419 (test-case "arity+keywords-combine/and" 420 (check-equal? (arity+keywords-combine/and) (arity+keywords (arity-at-least 0) '() #f)) 421 (check-equal? (arity+keywords-combine/and (arity+keywords '(4 9 16) '(#:a #:b) '(#:a #:b #:c))) 422 (arity+keywords '(4 9 16) '(#:a #:b) '(#:a #:b #:c))) 423 (check-equal? (arity+keywords-combine/and (arity+keywords '(1 2) '(#:a) '(#:a #:b #:c #:d)) 424 (arity+keywords '(2 3) '(#:b) '(#:a #:b #:c #:e))) 425 (arity+keywords 2 '(#:a #:b) '(#:a #:b #:c))) 426 (check-match (arity+keywords-combine/and (arity+keywords 0 '(#:a) #f) 427 (arity+keywords 0 '() '())) 428 (arity+keywords '() _ _))) 429 430 (test-case "arity+keywords-subtract" 431 (check-equal? (arity+keywords-subtract (arity+keywords 0 '() '()) 0 '()) 432 (arity+keywords 0 '() '())) 433 (check-equal? (arity+keywords-subtract (arity+keywords 1 '() '()) 0 '()) 434 (arity+keywords 1 '() '())) 435 (check-equal? (arity+keywords-subtract (arity+keywords 1 '() '()) 1 '()) 436 (arity+keywords 0 '() '())) 437 (check-equal? (arity+keywords-subtract (arity+keywords '(0 1) '() '()) 0 '()) 438 (arity+keywords '(0 1) '() '())) 439 (check-equal? (arity+keywords-subtract (arity+keywords '(0 1) '() '()) 1 '()) 440 (arity+keywords 0 '() '())) 441 (check-equal? (arity+keywords-subtract (arity+keywords '(3 7 10) '() '()) 5 '()) 442 (arity+keywords '(2 5) '() '())) 443 (check-equal? (arity+keywords-subtract (arity+keywords (arity-at-least 0) '() '()) 5 '()) 444 (arity+keywords (arity-at-least 0) '() '())) 445 (check-equal? (arity+keywords-subtract (arity+keywords (arity-at-least 49) '() '()) 57 '()) 446 (arity+keywords (arity-at-least 0) '() '())) 447 (check-equal? (arity+keywords-subtract (arity+keywords (arity-at-least 57) '() '()) 49 '()) 448 (arity+keywords (arity-at-least 8) '() '())) 449 (check-equal? (arity+keywords-subtract (arity+keywords (list 3 7 10 (arity-at-least 47)) '() '()) 450 5 '()) 451 (arity+keywords (list 2 5 (arity-at-least 42)) '() '())) 452 453 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) #f) 0 '()) 454 (arity+keywords 0 '(#:a #:b) #f)) 455 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) #f) 0 '(#:a)) 456 (arity+keywords 0 '(#:b) #f)) 457 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) #f) 0 '(#:b)) 458 (arity+keywords 0 '(#:a) #f)) 459 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) #f) 0 '(#:a #:b)) 460 (arity+keywords 0 '() #f)) 461 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 0 '()) 462 (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d))) 463 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 0 '(#:a)) 464 (arity+keywords 0 '(#:b) '(#:b #:c #:d))) 465 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 0 '(#:b)) 466 (arity+keywords 0 '(#:a) '(#:a #:c #:d))) 467 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 0 '(#:c)) 468 (arity+keywords 0 '(#:a #:b) '(#:a #:b #:d))) 469 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 0 '(#:d)) 470 (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c))) 471 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 472 0 '(#:a #:b)) 473 (arity+keywords 0 '() '(#:c #:d))) 474 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 475 0 '(#:a #:c)) 476 (arity+keywords 0 '(#:b) '(#:b #:d))) 477 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 478 0 '(#:a #:d)) 479 (arity+keywords 0 '(#:b) '(#:b #:c))) 480 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 481 0 '(#:b #:c)) 482 (arity+keywords 0 '(#:a) '(#:a #:d))) 483 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 484 0 '(#:b #:d)) 485 (arity+keywords 0 '(#:a) '(#:a #:c))) 486 (check-equal? (arity+keywords-subtract (arity+keywords 0 '(#:a #:b) '(#:a #:b #:c #:d)) 487 0 '(#:c #:d)) 488 (arity+keywords 0 '(#:a #:b) '(#:a #:b))) 489 ) 490 491 ) 492 493