www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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