Constrained genericity: Difference between revisions

m (=={{header|Racket}}== stub added... code comes in a moment.)
Line 777:
 
=={{header|Racket}}==
 
<code>edible&lt;%></code> objects simply need to state that they implement the
interface in the second argument to <code>class*</code>. By doing so they will
be forced to implement <code>eat</code>.
 
<lang 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?))</lang>
 
All the tests pass. Look at the tests to see what generates an exception (i.e.
not allowed at runtime) and what does not.
 
=={{header|Sather}}==
569

edits