#lang racket/load

(module a racket/base
  (require racket/generic)

  (define-generics simple-dict
    (dict-ref    simple-dict key [default])
    (dict-set    simple-dict key val)
    (dict-remove simple-dict key))

  (provide dict-ref
           dict-set
           dict-remove
           gen:simple-dict
           simple-dict?
           simple-dict/c))

(module b racket/base
  (require 'a racket/contract)

  ;; same as in "alist.rkt"
  (define-struct alist (v)
    #:methods gen:simple-dict
    [(define (dict-ref dict key
                       [default (lambda () (error "key not found" key))])
       (cond [(assoc key (alist-v dict)) => cdr]
             [else (if (procedure? default) (default) default)]))
     (define (dict-set dict key val)
       (alist (cons (cons key val) (alist-v dict))))
     (define (dict-remove dict key)
       (define al (alist-v dict))
       (remove* (assoc key al) al))])

  (provide/contract
   [make-alist
    (-> list?
        (simple-dict/c
         [dict-ref (->* (simple-dict? symbol?) (any/c) integer?)]
         [dict-set (-> simple-dict? symbol? integer? simple-dict?)]
         [dict-remove (-> simple-dict? symbol? simple-dict?)]))]))

(module c racket/base
  (require 'a 'b rackunit)

  (define dict (make-alist '((a . 5) (b . 10))))
  (check-equal? (dict-ref dict 'a) 5)
  (check-equal? (dict-ref dict 'b) 10)
  (check-exn exn:fail:contract?
             (λ () (dict-set dict 'a "bad")))
  (check-exn exn:fail:contract?
             (λ () (dict-set dict "bad" 5))))

(require 'c)

;; ----------------------------------------

(module contract-on-method racket
  (require racket/generic)
  (provide gen:foo foo? foo/c (contract-out [bar (foo? . -> . any/c)]))
  (define-generics foo
    (bar foo)))

(module make-generic-contract racket
  (require 'contract-on-method)
  (define (foo/c* c)
    (foo/c
     [bar (foo? . -> . c)])))


