87 lines
3.0 KiB
Racket
87 lines
3.0 KiB
Racket
#lang racket
|
|
(module+ test (require tests/eli-tester))
|
|
|
|
;; This is all that an object should need to properly implement.
|
|
(define edible<%>
|
|
(interface () [eat (->m void?)]))
|
|
|
|
(define (generic-container<%> containee/c)
|
|
(interface ()
|
|
[contents (->m (listof containee/c))]
|
|
[insert (->m containee/c void?)]
|
|
[remove-at (->m exact-nonnegative-integer? containee/c)]
|
|
[count (->m exact-nonnegative-integer?)]))
|
|
|
|
(define ((generic-box-mixin containee/c) %)
|
|
(->i ([containee/c contract?])
|
|
(rv (containee/c) (implementation?/c (generic-container<%> containee/c))))
|
|
(class* % ((generic-container<%> containee/c))
|
|
(super-new)
|
|
(define l empty)
|
|
(define/public (contents) l)
|
|
(define/public (insert o) (set! l (cons o l)))
|
|
(define/public (remove-at i)
|
|
(begin0 (list-ref l i)
|
|
(append (take l i) (drop l (add1 i)))))
|
|
(define/public (count) (length l))))
|
|
|
|
;; As I understand it, a "Food Box" from the task is still a generic... i.e.
|
|
;; you will specify it down ;; to an "apple-box%" so: food-box%-generic is still
|
|
;; generic. food-box% will take any kind of food.
|
|
(define/contract (food-box-mixin T%)
|
|
(-> (or/c (λ (i) (eq? edible<%> i)) (implementation?/c edible<%>))
|
|
(make-mixin-contract))
|
|
(generic-box-mixin (and/c (is-a?/c edible<%>) (is-a?/c T%))))
|
|
|
|
(module+ test
|
|
|
|
(define integer-box% ((generic-box-mixin integer?) object%))
|
|
(define integer-box (new integer-box%))
|
|
|
|
(define apple%
|
|
(class* object% (edible<%>)
|
|
(super-new)
|
|
(define/public (eat)
|
|
(displayln "nom!"))))
|
|
|
|
(define banana%
|
|
(class* object% (edible<%>)
|
|
(super-new)
|
|
(define/public (eat)
|
|
(displayln "peel.. peel... nom... nom!"))))
|
|
|
|
(define semolina%
|
|
(class* object% () ; <-- empty interfaces clause
|
|
(super-new)
|
|
;; you can call eat on it... but it's not explicitly (or even vaguely)
|
|
;; edible<%>
|
|
(define/public (eat) (displayln "blech!"))))
|
|
|
|
;; this will take any object that is edible<%> and edible<%> (therefore all
|
|
;; edible<%> objects)
|
|
(define any-food-box (new ((food-box-mixin edible<%>) object%)))
|
|
|
|
;; this will take any object that is edible and an apple<%>
|
|
;; (therefore only apple<%>s)
|
|
(define apple-food-box (new ((food-box-mixin apple%) object%)))
|
|
|
|
(test
|
|
;; Test generic boxes
|
|
(send integer-box insert 22)
|
|
(send integer-box insert "a string") =error> exn:fail:contract?
|
|
|
|
;; Test the food box that takes any edible<%>
|
|
(send any-food-box insert (new apple%))
|
|
(send any-food-box insert (new banana%))
|
|
(send any-food-box insert (new semolina%)) =error> exn:fail:contract?
|
|
|
|
;; Test the food box that takes any apple%
|
|
(send apple-food-box insert (new apple%))
|
|
(send apple-food-box insert (new banana%)) =error> exn:fail:contract?
|
|
(send apple-food-box insert (new semolina%)) =error> exn:fail:contract?
|
|
(send apple-food-box count) => 1
|
|
|
|
;; Show that you cannot make a food-box from the non-edible<%> semolina cannot
|
|
(implementation? semolina% edible<%>) => #f
|
|
(new ((food-box-mixin semolina%) object%)) =error> exn:fail:contract?))
|